Skip to content

Bug: UTF-8 char-boundary panic in summarize_patch_for_logging on non-ASCII patches #35381

Description

@afjal1

What version of Codex CLI is running?

Reproduced with codex-cli 0.145.0. The same implementation is still present in main at e4fb5311d7468839def62eabda4b268f4a54cf11.

What platform is your computer?

Darwin 26.5.2 arm64 arm (macOS).

What issue are you seeing?

summarize_patch_for_logging panics when a patch contains non-ASCII characters (e.g., file paths with UTF-8 characters like Chinese, Japanese, accented Latin, emoji) and the truncation point falls in the middle of a multi-byte character.

Root cause: Line 901 uses a byte-level slice:

let head_trunc = if head.len() > 800 {
    format!("{}…", &head[..800])  // panics if byte 800 is not a char boundary
} else {
    head
};

&head[..800] is a byte-level index. If the 800th byte falls in the middle of a multi-byte UTF-8 character (e.g., a 3-byte CJK character, a 4-byte emoji), Rust panics with:

byte index 800 is not a char boundary; it is inside '某' (bytes 798..801)

This is a logging function (summarize_patch_for_logging), so it won't lose user data, but it will abort the logging call and could unwind a ? chain upstream depending on the call site.

What steps can reproduce the bug?

  1. Create a file with a non-ASCII name or content that exceeds 800 bytes when the first 20 lines are joined
  2. Use apply_patch to modify that file
  3. The logging function panics when truncating

Alternatively, construct a patch string where bytes 798-800 contain a multi-byte character and call summarize_patch_for_logging directly.

What is the expected behavior?

The truncation should respect UTF-8 character boundaries. The function should truncate at the last complete character before byte 800.

Suggested fix

Replace &head[..800] with a char-boundary-safe truncation. Two options:

Option A (using floor_char_boundary, stable since Rust 1.73):

let head_trunc = if head.len() > 800 {
    let boundary = head.floor_char_boundary(800);
    format!("{}…", &head[..boundary])
} else {
    head
};

Option B (using char_indices, already used elsewhere in the codebase):

let head_trunc = if head.len() > 800 {
    let boundary = head.char_indices()
        .take_while(|(i, _)| *i <= 800)
        .last()
        .map(|(i, _)| i)
        .unwrap_or(800);
    format!("{}…", &head[..boundary])
} else {
    head
};

The Rust toolchain is pinned to 1.95.0, so floor_char_boundary is available and is the cleanest option.

Related

Similar class of bug to #34282 (rollout trace reducer panics on non-ASCII JSON truncation). This suggests a pattern of byte-level truncation across the codebase that should be audited for UTF-8 safety.

Scope

Single-line fix in one file (cloud-tasks-client/src/http.rs:901). No behavioral change — only the truncation boundary is made UTF-8-safe.

Metadata

Metadata

Assignees

No one assigned

    Labels

    CLIIssues related to the Codex CLIbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions