Bug: UTF-8 char-boundary panic in `summarize_patch_for_logging` on non-ASCII patches
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?
- Create a file with a non-ASCII name or content that exceeds 800 bytes when the first 20 lines are joined
- Use
apply_patchto modify that file - 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.
1 Comment
I reproduced this independently on Linux against current
main(20dafe201d91). With 798 ASCII bytes followed by某, byte 800 is not a character boundary andsummarize_patch_for_loggingpanics as reported.While checking the nearby truncation paths, I found two more byte slices with the same UTF-8 failure mode:
cloud-tasks-client/src/http.rs:874-878:tailslices ats.len() - max, which can land inside a multibyte character.cloud-tasks/src/lib.rs:2123:&raw[..320]has the same prefix-truncation risk.The workspace already has
codex_utils_string::take_bytes_at_char_boundaryfor safe prefix truncation. If the team would like these handled together or as separate scoped fixes, I would be happy to prepare the invited PR and regression coverage.