claude-code - 💡(How to fix) Fix Auto-memory subsystem writes to MEMORY.md mid-turn, invalidating Edit-tool read snapshots

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The auto-memory subsystem in Claude Code writes to ~/.claude/projects/<slug>/memory/MEMORY.md between turns (and possibly mid-turn) without notifying the read-snapshot tracker. When the model Reads MEMORY.md early in a turn and then Edits it later, the edit fails with:

<tool_use_error>File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.</tool_use_error>

The "user or linter" framing is wrong for this trigger — the harness modified its own file.

Error Message

T+0 Read MEMORY.md T+30s (memory save happens silently — Write to some memory/<topic>.md file) T+45s Edit MEMORY.md (intends to add index pointer) → ERROR: "modified since read" T+50s Read MEMORY.md ← forced recovery T+55s Edit MEMORY.md → OK

Root Cause

The auto-memory subsystem is on by default. The error class it triggers is treated by the model as something the user did (per the reminder's wording — "either by the user or by a linter"). This is misleading:

  • The model spends tokens explaining the error to the user.
  • The model spawns a recovery Read that wasn't logically required.
  • For agentic / orchestration workflows, the failed Edit can cascade into broken state.
  • The error message blames the user for a write the harness performed.

Code Example

<tool_use_error>File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.</tool_use_error>

---

T+0    Read MEMORY.md
T+30s  (memory save happens silently — Write to some memory/<topic>.md file)
T+45s  Edit MEMORY.md (intends to add index pointer)
ERROR: "modified since read"
T+50s  Read MEMORY.md  ← forced recovery
T+55s  Edit MEMORY.mdOK

---

<system-reminder>
Note: /Users/<user>/.claude/projects/<slug>/memory/MEMORY.md was modified,
either by the user or by a linter. This change was intentional, so make
sure to take it into account as you proceed...
</system-reminder>

---

<tool_use_error>
File was modified by Claude Code's auto-memory subsystem after your last read.
Read it again before attempting to write.
</tool_use_error>

---

def harness_internal_write(path, content):
    write(path, content)
    snapshot_tracker.refresh(path)
RAW_BUFFERClick to expand / collapse
<!-- Draft NEW issue to file at https://github.com/anthropics/claude-code/issues Post with: gh issue create --repo anthropics/claude-code \ --title "Auto-memory subsystem writes to MEMORY.md mid-turn, invalidating Edit-tool read snapshots" \ --body-file new-issue-memory-md-automemory.md \ --label bug Note: this is NOT a duplicate of #3513. #3513's trigger is user-configured PostToolUse hooks. This issue's trigger is the harness's own auto-memory subsystem, which fires WITHOUT any user-configured hook. The two bugs share a symptom but have independent triggers and require independent fixes. -->

Summary

The auto-memory subsystem in Claude Code writes to ~/.claude/projects/<slug>/memory/MEMORY.md between turns (and possibly mid-turn) without notifying the read-snapshot tracker. When the model Reads MEMORY.md early in a turn and then Edits it later, the edit fails with:

<tool_use_error>File has been modified since read, either by the user or by a linter. Read it again before attempting to write it.</tool_use_error>

The "user or linter" framing is wrong for this trigger — the harness modified its own file.

Not a duplicate of #3513

#3513 covers the case where user-configured PostToolUse hooks rewrite the file. This issue covers a different trigger that is part of the Claude Code harness itself:

  • #3513 trigger: User-configured PostToolUse hooks (prettier --write, dart format, markdown-formatter.py, etc.) modify the file after Edit/Write completes.
  • This issue's trigger: The auto-memory subsystem fires Write against MEMORY.md outside the model's tool-call sequence, often between turns. The user has no hook configured; the harness owns this write.

Both produce identical errors. Both need fixes. They are independent.

Repro

  1. Use auto-memory (default on, file at ~/.claude/projects/<slug>/memory/MEMORY.md).
  2. In a session: Read MEMORY.md early to scan the index.
  3. Later in the same session, after the model has saved a new memory file via Write memory/something.md, attempt Edit MEMORY.md to add the index pointer.
  4. The Edit fails with "modified since read".

Or, simpler:

  1. Start a session. The session-start system reminder fires: Note: MEMORY.md was modified, either by the user or by a linter. Read it again before attempting to write it.
  2. No user action. No hook configured. The previous turn's auto-memory save updated MEMORY.md between turns.

Evidence

Scan of 3 weeks of JSONL transcripts (single user, macOS): MEMORY.md is the top hit across the 22 sessions hitting this error class, accounting for ~6 of them. Recovery pattern observed across sessions:

T+0    Read MEMORY.md
T+30s  (memory save happens silently — Write to some memory/<topic>.md file)
T+45s  Edit MEMORY.md (intends to add index pointer)
       → ERROR: "modified since read"
T+50s  Read MEMORY.md  ← forced recovery
T+55s  Edit MEMORY.md → OK

The recovery Read succeeds and returns content the model has not seen change — confirming the modification was content-equivalent or invisibly small, with the only observable effect being the mtime bump that invalidated the snapshot.

In the most recent turn of my current session, the <system-reminder> fired at turn-start (before any tool call), indicating the inter-turn write happened with no in-turn trigger:

<system-reminder>
Note: /Users/<user>/.claude/projects/<slug>/memory/MEMORY.md was modified,
either by the user or by a linter. This change was intentional, so make
sure to take it into account as you proceed...
</system-reminder>

No user edit. No hook. The harness's own auto-memory subsystem.

Why this matters

The auto-memory subsystem is on by default. The error class it triggers is treated by the model as something the user did (per the reminder's wording — "either by the user or by a linter"). This is misleading:

  • The model spends tokens explaining the error to the user.
  • The model spawns a recovery Read that wasn't logically required.
  • For agentic / orchestration workflows, the failed Edit can cascade into broken state.
  • The error message blames the user for a write the harness performed.

Proposed mechanism

Two independent fixes — either suffices, both are good:

Fix A (preferred): synchronize the auto-memory subsystem with the read-snapshot tracker

When auto-memory writes to MEMORY.md:

  1. Acquire the same lock the tool layer uses for Edit/Write.
  2. After the write, refresh the read-snapshot for MEMORY.md — both mtime and content hash.

This treats auto-memory writes as part of the harness's own internal coherence, not as foreign modifications. The model's next Edit succeeds without spurious failures, and a content drift (rare) would surface as a normal content-mismatch error.

Fix B (broader): clarify the error message and add provenance

If the harness can identify that the file was modified by its own subsystem (auto-memory, hooks, indexer), the error should say so:

<tool_use_error>
File was modified by Claude Code's auto-memory subsystem after your last read.
Read it again before attempting to write.
</tool_use_error>

This doesn't fix the spurious failure but stops blaming the user. Worth doing regardless of Fix A.

Relationship to #3513

#3513 needs a snapshot-refresh after PostToolUse hooks. This issue needs a snapshot-refresh after auto-memory writes. The underlying mechanism is the same (refresh the snapshot when the harness itself modifies the file) but the trigger surface is different. A clean fix would route both through a single internal API:

def harness_internal_write(path, content):
    write(path, content)
    snapshot_tracker.refresh(path)

…and have both auto-memory and the PostToolUse runner call it.

Environment

  • macOS 26
  • Claude Code CLI 2026-05 vintage
  • Auto-memory enabled (default)
  • ~/.claude/CLAUDE.md + project CLAUDE.md + auto-loaded MEMORY.md
  • Reproduces with and without user-configured hooks

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING