Skip to content

fix(file): don't flag UTF-8 files as binary on truncated read sample - #81493

Closed
branchingjade wants to merge 1 commit into
NousResearch:mainfrom
branchingjade:fix/read-file-ufffd-truncation
Closed

fix(file): don't flag UTF-8 files as binary on truncated read sample#81493
branchingjade wants to merge 1 commit into
NousResearch:mainfrom
branchingjade:fix/read-file-ufffd-truncation

Conversation

@branchingjade

Copy link
Copy Markdown

Summary

read_file uses a 1000-byte head -c sample to decide whether a file is binary. When the 1000th byte splits a UTF-8 multi-byte character, the truncated tail decodes (with errors="replace") to exactly one U+FFFD — even in perfectly legitimate UTF-8 text. This is very common with CJK files (3 bytes per char).

The old check ("\ufffd" in sample) treated any U+FFFD as binary, so large CJK text files (>= ~333 chars) were permanently flagged binary/read-only.

Change

Allow exactly one U+FFFD in the sample: sample.count("\ufffd") > 1 flags binary. Genuine decode failures (GBK bytes read as UTF-8, latin-1 decoded with replace) produce many replacement chars and are still caught.

Tests

Added to tests/tools/test_file_operations.py::TestReadNonUtf8IsBinary:

  • test_truncated_cjk_tail_single_ufffd_not_flagged — 333 CJK chars (999 bytes) + truncated char tail → 1 U+FFFD, must NOT be binary
  • test_many_ufffd_still_flagged_binary — mojibake with dozens of U+FFFD still flagged

Verified: 6/6 binary-related tests pass; full-file run shows no new failures vs. base (8 pre-existing Windows-only failures: POSIX file-mode + symlink tests).

head -c 1000 can split a UTF-8 multi-byte char at the 1000-byte boundary;
the truncated tail decodes to a single U+FFFD even in legitimate UTF-8
text (common with CJK files). The old check treated any U+FFFD as binary,
making large CJK text files read-only.

Allow exactly one U+FFFD in the sample: genuine decode failures (e.g.
GBK bytes read as UTF-8) produce many replacement chars and are still
caught by the >1 threshold.
@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) sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades duplicate This issue or pull request already exists labels Aug 8, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #76925: this is the same byte-sample UTF-8 boundary repair for a synthetic trailing U+FFFD. #76925 is the earlier open canonical implementation.

@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 sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades 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