Skip to content

Bug: #[serde(rename_all = "camelCase")] panics on struct fields with non-ASCII (e.g., CJK) identifiers #2953

@fswdev

Description

@fswdev

Hello Serde Team,

I've encountered a compile-time panic when using #[derive(Deserialize)] in combination with #[serde(rename_all = "camelCase")] on a struct that has fields with non-ASCII identifiers (specifically, Chinese characters).

Summary

The serde_derive procedural macro appears to panic during compilation when it tries to apply the camelCase renaming rule to a field name containing multi-byte UTF-8 characters. The error message clearly indicates a byte-indexing issue on a non-char boundary, suggesting the renaming logic might be incorrectly assuming ASCII strings.

The issue does not occur if:

  • The #[serde(rename_all = "camelCase")] attribute is removed.
  • The attribute is changed to #[serde(rename_all = "PascalCase")].

This suggests the bug is specific to the implementation of the camelCase transformation logic.

Minimal Reproducible Example (MRE)

Here is the smallest possible Rust project that demonstrates the bug:

Cargo.toml:

[package]
name = "serde-cjk-bug-report"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { version = "1.0.219", features = ["derive"] }

src/main.rs:

use serde::Deserialize;

// This struct definition causes the compiler to panic.
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct TestPayload {
    // Field name with Chinese characters
    pub 项目名称: String,
    pub 项目地址: String,
}

fn main() {
    // The panic happens at compile time, so the main function's content
    // doesn't matter, but it's here to make the example complete.
    println!("This code will not compile.");
}

Compiler Panic Output

When trying to compile the MRE with cargo build, the compiler panics with the following output:

error: proc-macro derive panicked
  --> src/main.rs:5:10
   |
5  | #[derive(Deserialize, Debug, Clone)]
   |          ^^^^^^^^^^^
   |
   = help: message: byte index 1 is not a char boundary; it is inside '项' (bytes 0..3) of `项目名称`

Expected vs. Actual Behavior

  • Expected Behavior: The code should compile successfully. The camelCase rule, when applied to a field name like 项目名称 which has no case or separators to modify, should ideally result in no change to the name, or at least not cause a panic.
  • Actual Behavior: The compiler panics with a fatal error related to string indexing.

Analysis

The panic message byte index 1 is not a char boundary strongly suggests that the string manipulation logic within the rename_all = "camelCase" implementation is incorrectly treating the UTF-8 field identifier as a sequence of bytes rather than characters. It attempts a byte-level index access, which is unsafe for multi-byte strings. A correct implementation should iterate over chars() rather than bytes().

Workarounds

For others encountering this issue, the current workarounds are:

  1. Avoid using non-ASCII field names if rename_all is needed.
  2. Use ASCII field names and the #[serde(rename = "项目名称")] attribute on each field individually.
  3. Remove the rename_all attribute if the exact casing is not critical for your use case.

Environment

  • Rust Version: rustc 1.89.0 (29483883e 2025-08-04)
  • Serde Version: 1.0.219
  • OS: Windows 10

Thank you for maintaining this essential crate! I hope this report is helpful in resolving the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions