Phantom Reads: Claude Does Not Follow Up on Persisted Output Read Results
Update (Jan 30, 2026): This investigation has evolved significantly since the original filing. Key corrections: phantom reads affect all tested versions (2.0.54 through 2.1.22), not just 2.0.59+. The 2.0.58 boundary was a small-sample artifact — Era 1 versions use a different mechanism ([Old tool result content cleared]) but are equally affected. Most importantly, phantom read behavior is governed by server-side state, not client build version — the same build can show 100% failure one day and 100% success the next. See later messages and investigation repo for current findings, reproduction steps, and a validated workaround.
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Summary
When the Read tool returns a <persisted-output> response (indicating the result was saved to a file due to size), Claude proceeds as if it successfully read the file contents rather than making a follow-up Read call to retrieve the persisted output. These "Phantom Reads" causes Claude to operate on files it has never actually seen, though it appears to proceed normally.
- This is highly reproducible in 2.0.59 and later (71% of prompts requesting reads)
- This is not reproducible in 2.0.58 or earlier
The experiment was conducted on the following builds. Results show failures out of total attempts:
- 2026-01-08 - 2.1.2: 1 of 2
- 2025-12-23 - 2.0.76: 2 of 2
- 2025-12-02 - 2.0.62: 1 of 2
- 2025-11-30 - 2.0.60: 4 of 4
- 2025-11-29 - 2.0.59: 2 of 4
- ---------------------------
- 2025-11-28 - 2.0.58: 0 of 4
- 2025-11-26 - 2.0.56: 0 of 4
- 2025-11-10 - 2.0.54: 0 of 4
Environment
- Claude Code version: various, investigation began with 2.1.3
- OS: macOS (Darwin 25.2.0)
- Model: claude-opus-4-5-20251101
Steps to Reproduce
- Start a Claude Code session with a project containing multiple markdown files of varying sizes
- Ask Claude to perform a task that requires reading several files (e.g., "Review this specification document and cross-reference with these related files")
- Observe that some Read tool calls return:
```
<persisted-output>Tool result saved to: /Users/[user]/.claude/projects/[project-id]/tool-results/[tool-id].txt
Use Read to view</persisted-output>
```
- Claude proceeds with analysis without making follow-up Read calls to retrieve the persisted content
Expected Behavior
When Claude receives a <persisted-output> response, it should:
- Recognize that the file contents were not returned inline
- Automatically make a follow-up Read call to the persisted output file path
- Only proceed with analysis after successfully retrieving the actual file contents
Actual Behavior
Claude receives the <persisted-output> message and:
- Does not recognize this as an incomplete read
- Proceeds with the task as if the file was successfully read
- May confabulate or make assumptions about file contents it never saw
- Sometimes states "I've read the file" when it has not
Reproduction Case Study
In a series of test sessions, Claude was asked to review a ticket file and cross-reference with related specifications. The following occurred:
First batch of parallel Read calls:
| File | Result | Follow-up? |
| ------------------ | -------------------------- | ---------- |
| ticket.md | <persisted-output> | No |
| Schema-A.md | <persisted-output> | No |
| Schema-B.md | <persisted-output> | No |
| Spec-Overview.md | Actual content (678 lines) | N/A |
After this batch, Claude stated "Good, I've read the ticket" despite never having seen its contents.
Claude only obtained actual file contents later when it happened to re-read some files in subsequent tool calls (which returned actual content rather than persisted-output).
Impact
- Severity: High - Claude operates on phantom data
- Frequency: Intermittent - depends on file sizes and system state. 71% of prompts reported at least one "phantom read" in experiments on builds 2.0.59 and later. 0% of prompts reported the issue in 2.0.58 or earlier
- User Impact:
- Users receive analysis/recommendations based on files Claude never read
- Difficult to detect because Claude confidently proceeds as if it read the files
- Can cause incorrect code changes, missed issues, or hallucinated file contents
What Should Happen?
Expected Behavior
When Claude receives a <persisted-output> response, it should:
- Recognize that the file contents were not returned inline
- Automatically make a follow-up Read call to the persisted output file path
- Only proceed with analysis after successfully retrieving the actual file contents
Error Messages/Logs
Steps to Reproduce
Reproduction Case Study
In a series of test sessions, Claude was asked to review a ticket file and cross-reference with related specifications. The following occurred:
First batch of parallel Read calls:
| File | Result | Follow-up? |
| ------------------ | -------------------------- | ---------- |
| ticket.md | <persisted-output> | No |
| Schema-A.md | <persisted-output> | No |
| Schema-B.md | <persisted-output> | No |
| Spec-Overview.md | Actual content (678 lines) | N/A |
After this batch, Claude stated "Good, I've read the ticket" despite never having seen its contents.
Claude only obtained actual file contents later when it happened to re-read some files in subsequent tool calls (which returned actual content rather than persisted-output).
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.0.58
Claude Code Version
2.1.3 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
_No response_
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗