Phantom Reads: Claude Does Not Follow Up on Persisted Output Read Results

Resolved 💬 6 comments Opened Jan 11, 2026 by rcgray Closed Mar 1, 2026

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

  1. Start a Claude Code session with a project containing multiple markdown files of varying sizes
  2. Ask Claude to perform a task that requires reading several files (e.g., "Review this specification document and cross-reference with these related files")
  3. 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>
```

  1. 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:

  1. Recognize that the file contents were not returned inline
  2. Automatically make a follow-up Read call to the persisted output file path
  3. Only proceed with analysis after successfully retrieving the actual file contents

Actual Behavior

Claude receives the <persisted-output> message and:

  1. Does not recognize this as an incomplete read
  2. Proceeds with the task as if the file was successfully read
  3. May confabulate or make assumptions about file contents it never saw
  4. 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:

  1. Recognize that the file contents were not returned inline
  2. Automatically make a follow-up Read call to the persisted output file path
  3. 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_

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗