Skip to content

fix: CJK UTF-8 source files misclassified as binary when head -c 1000 truncates mid-character - #81098

Closed
MISAKIGA wants to merge 1 commit into
NousResearch:mainfrom
MISAKIGA:fix/utf8-truncation-binary-misclassify
Closed

fix: CJK UTF-8 source files misclassified as binary when head -c 1000 truncates mid-character#81098
MISAKIGA wants to merge 1 commit into
NousResearch:mainfrom
MISAKIGA:fix/utf8-truncation-binary-misclassify

Conversation

@MISAKIGA

@MISAKIGA MISAKIGA commented Aug 7, 2026

Copy link
Copy Markdown

Summary

read_file misclassifies valid UTF-8 source files containing CJK (or any multi-byte) characters as binary, refusing to display them.

Root cause

read_file samples a file with head -c 1000 and passes the decoded output to _is_likely_binary(). The content-analysis path treats any occurrence of U+FFFD (replacement char) as binary — a guard against lossy read→edit→write round-trips on genuinely non-UTF-8 files.

The guard's assumption ("legitimate UTF-8 text effectively never contains U+FFFD") holds for a complete file, but not for a truncated sample: when byte 1000 lands inside a 3- or 4-byte CJK character, the incomplete sequence decodes as exactly one trailing U+FFFD — a sampling artifact.

Impact

A real-world case: hermes-shine/front/src/pages/chat/store.ts (24,892 bytes, valid UTF-8 with Chinese comments) became unreadable via read_file because its byte 1000 splits the 3-byte character 会 (E4 BC 9A).

Fix

Strip at most one U+FFFD from the tail of the sample before judging. This precisely targets the truncation artifact:

  • Genuine non-UTF-8 files have replacement chars throughout the sample, not just at the tail → still flagged binary (safety net preserved).
  • Valid UTF-8 text truncated mid-character ends with exactly one U+FFFD → artifact removed → readable as text.

Tests

Added 4 unit tests in tests/tools/test_file_operations_edge_cases.py:

Test Verifies
test_truncated_multibyte_utf8_not_flagged CJK text with trailing truncation artifact → NOT binary
test_trailing_fffd_from_real_cjk_file 999 ASCII + orphaned UTF-8 lead byte → NOT binary
test_internal_replacement_chars_still_binary U+FFFD spread through sample (real corruption) → STILL binary
test_only_trailing_replacement_char_stripped Middle corruption → binary; lone trailing artifact → text

Verification

  • tests/tools/test_file_operations.py tests/tools/test_file_operations_edge_cases.py: 71 passed (incl. the 4 new + pre-existing U+FFFD guard tests)
  • Full tests/tools/ suite: 5112 passed, 228 pre-existing failures in unrelated modules (approval/delegation/daytona/browser — environment-dependent, zero in file ops)
  • End-to-end with real LocalEnvironment: the previously-unreadable chat/store.ts now returns is_binary: False and full content

… truncates mid-character

The _is_likely_binary() content-analysis path rejects any sample
containing U+FFFD (replacement char) under the assumption that
'legitimate UTF-8 text effectively never contains U+FFFD'.  This
assumption holds for a complete file read, but not for the head -c
1000 sample that read_file uses: when byte 1000 lands inside a
multi-byte CJK character, the truncated sequence decodes as exactly
one trailing U+FFFD -- a sampling artifact, not binary content.

Fix: strip at most one U+FFFD from the tail of the sample before
judging.  A real non-UTF-8 file has replacement chars throughout
the sample (not just at the tail), so the binary-detection safety
net is preserved.

Before: chat/store.ts (24892 bytes of valid UTF-8 with CJK comments)
        → head -c 1000 cuts '会' (E4 BC 9A) → '\ufffd' in sample
        → is_binary=True → 'Binary file - cannot display as text'

After:  the trailing artifact is stripped; the rest of the sample is
        clean → is_binary=False → file is readable as text.
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/tools Tool registry, model_tools, toolsets tool/file File tools (read, write, patch, search) duplicate This issue or pull request already exists labels Aug 7, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #79408: both implement the same trailing-U+FFFD exemption for a valid UTF-8 byte-sample boundary cut. Please consolidate review and tests there.

@teknium1

teknium1 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Closing — superseded by #81961, which fixed this bug class (1000-byte sample cutting a multibyte char → binary false positive) at the byte layer across read_file, patch, and search_files, with regression tests for truncated-CJK/BOM/UTF-16/NUL cases. ~24 PRs raced on this one; earliest diagnosis credit to @webtecnica (#76924), merged implementation from @ayushnangia (#80440). Thanks for jumping on it.

@teknium1 teknium1 closed this Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists tool/file File tools (read, write, patch, search) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants