[Bug] Edit tool silently truncates files after multiple block replacements
Bug Description
Bug report — Edit tool silently truncates files after multiple block-style replacements
Environment: Cowork desktop (Claude Agent SDK), Windows 11 Date observed: 2026-04-24 Severity: High — silent data loss with no error surfaced to the agent or user.
Summary
When the Edit tool is called repeatedly on the same large JavaScript file with multi-line old_string / new_string replacements, after roughly 3 successful calls the tool reports "The file has been updated successfully" but the file on disk is truncated mid-content (mid-statement, mid-line, mid-word). No error is raised. The Read tool, called immediately after, returns the pre-truncation cached contents for several seconds, so the agent has no way to detect the corruption until a separate process (bash, node) reads the actual file.
Reproduction
- Start with a valid JS file ~13 KB / ~290 lines (e.g., an Azure Function handler).
- Call Edit tool three times in sequence, each replacing a block of roughly 15–60 lines with a new block of similar size, all with unique old_strings present verbatim in the file.
- After the third call, run wc -l and node -c via bash on the same path.
Observed: the file is truncated at a position unrelated to any edit (mid-line, often mid-identifier). Syntax check fails with Unexpected end of input or Invalid or unexpected token. Each Edit call still returned success.
Minimal evidence from this session
=== Edit tool said ===
The file <...>/upload.js has been updated successfully. (file state is current in your context — no need to Read it back)
=== Actual disk state a moment later ===
$ wc -l upload.js
255 upload.js # was 293 before the Edit calls
$ node -c upload.js
SyntaxError: Unexpected end of input
at wrapSafe (node:internal/modules/cjs/loader:1638:18)
$ tail -1 upload.js | cat -A
return json(400, { error: Only these file types are allowed: ${Object.keys(ALLOWED_EXTENSIONS).join(', ')} });$
file ends here, mid-function, no closing braces, no final function json() helper
The file was truncated at byte offset ~11 500 in the middle of an if branch. Everything after was gone.
Scope
• Affected files observed in this session: upload.js, adminConfig.js, getContentUrl.js, serveContent.js, auth.js — all Azure Functions source files between 7 KB and 18 KB in size.
• Not path-dependent. Initially suspected the parent path contained a Unicode LTR-mark (U+200E) and that the truncation was a Windows path-encoding issue. Moved files to a clean ASCII-only scratchpad path (outputs/functions-staging/) and re-ran. Same truncation reproduced there.
• Workaround: Python open()/write() and bash cat >> / sed applied on the exact same files, same path, same operations — worked correctly on the first try with no truncation. Syntax-check passed. 12 of 12 production files validated.
Why this is high-severity
- Silent failure. The Edit tool claims success. There is no status: truncated, no warning, no mismatch signal. An agent that finishes a task with 5 successful Edit calls has no reason to re-verify each file byte-by-byte.
- Trusted production code path. The user was editing live Azure Functions backing a client portal. Had the agent not run a node -c check afterwards, the broken files would have been committed and deployed.
- Read cache desynchronisation. After the truncation, Read returned the pre-truncation content for multiple turns. This actively misleads the agent into believing the file is fine. The agent only discovered the problem because bash/node view the real disk state, not the cached view.
- Non-obvious trigger. It isn't the first Edit call that fails, and it isn't tied to payload size. It appears to emerge after repeated calls on the same file, and survives across different folders.
Proposed triage
- Instrument Edit to compare file length before/after. After a successful write, re-read the first and last 256 bytes and confirm the new content contains both the prefix and suffix the agent expected. If not, return a hard error instead of "success".
- Invalidate Read cache on every Edit. The discrepancy between Read output and actual disk content is arguably the more dangerous half of this bug.
- Add a telemetry event (edit_tool.apparent_success_with_file_shrink) that fires when the post-edit file is more than N bytes smaller than the sum of expected changes, and surface it in Cowork logs. This will let Anthropic measure real-world incidence.
- Document in tool help that large JS file refactors should prefer a scripted approach (Python or bash heredoc) until the underlying bug is fixed.
Workaround we landed on
For the refactor we were doing (hoisting constants to the top of 5 files, renaming MAX_RETRIES → WRITE_MAX_RETRIES, aligning a 2 MB size limit), switching to a single Python str.replace script applied all five files' changes in one run with zero issues. The same replacements, staged through Edit across multiple turns, truncated all five.
Working pattern:
Applied from bash via pyt…
Note: Content was truncated.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗