[BUG] Cowork (cloud) re-serves stale local-file content within a session: after a file is staged once, re-staging the same path returns the OLD bytes while reporting the file's NEW size/mtime (false all-clear)

Status Open
Maintainer reply None cached
Activity 1 comment · opened Jul 20, 2026

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?

Re-staging a file path that was already staged earlier in the same Claude Desktop agent-mode
(Cowork) session returns the content the file had at first stage, even after the file has
changed on the device. The stale copy is complete and valid — it decodes, parses and
terminates correctly — so nothing in the session looks wrong.

Worse, the staging tool reports the device's current metadata (byte count and mtime)
while delivering the stale bytes. A caller that defensively compares reported size against
expected size sees agreement and proceeds on old content.

We reproduced this deterministically with a six-probe corpus and hash-level ground truth
(details below). Measured conclusions:

  • The cache key is path-only. Invalidation ignores size, mtime and content:
  • file grew 236 B → 1270 B: still served the 236 B version;
  • file shrank 341 B → 100 B: still served the 341 B version;
  • file changed content at an identical 379 B length: still served the old content;
  • device mtimes were fresh and were reported as fresh, yet bytes were stale.
  • Sticky: three re-stages in-session, plus a fourth ~14 minutes later — stale every time.

No retry recovers the file.

  • Scope: only paths staged earlier in the same session. A newly created path staged

for the first time is correct, and a device-native read (executing on the device instead of
staging) returns the current file. So the device is fine and the FUSE mount is fine — the
staleness is confined to the staging layer.

Real-world impact, twice, before we understood it:

  1. A code review nearly rejected a correct patch: the reviewing session was served a stale

dashboard_gen.py (101,286 B, an older complete version) while the tool reported the
device's 106,552 B
. It "read" the file, found the fix missing, and almost bounced it.

  1. A session was served stale STATE.md + DECISIONS.md handoff files and began planning

from a superseded project state.

What Should Happen?

Re-staging a path that has changed on the device should deliver the current file.

Failing that, the two acceptable fallbacks — in order of preference:

  1. Attach a validator to the cache entry (size + mtime, or a content hash) and revalidate

on re-stage. Size+mtime alone fixes the two size-changing cases; the same-size content
change additionally needs a content hash or an explicit invalidate-on-re-stage.

  1. At minimum, never report fresh metadata over stale bytes. If a cached copy is served,

the reported byte count/mtime must describe what was delivered, not what is on the
device. Today's behaviour destroys the only signal a client could act on — as it stands,
there is no in-session check that can detect the staleness.

Error Messages/Logs

**None — and that is the core of the report.** There is no error, no warning, no exception.
The stale file is complete, valid UTF-8, correctly terminated and parses normally. The only
observable is a hash comparison against the device, which requires an out-of-band channel.

Representative captured pair (probe P1):

device (ground truth, after change):  1270 bytes  md5 ee27ce3caa4464cf974034aedf38f82b  MARKER: P1-grow-v2-34031eed
staging tool REPORTED:                1270 bytes  (+ the device's fresh mtime)
bytes actually DELIVERED:              236 bytes  md5 e2c62e5a0e349e62610fe00148eab247  MARKER: P1-grow-v1-9bf861ce


The same-size probe (P3) is the sharpest case — reported, delivered and device sizes all
agree at 379 bytes, while the content is a version behind:

device (after change):  379 bytes  md5 c86d1d2f7bc9301bec007b7d65271e80  MARKER: P3-samesize-v2-b69a42a5
delivered:              379 bytes  md5 046fee935460aebe182a326151e80378  MARKER: P3-samesize-v1-c0939a6e

Steps to Reproduce

Requires two seats: one with native device filesystem access (mutator) and one
cloud/staging agent session (observer). A mount-backed session cannot reproduce this
and will read everything live — verify your observer actually stages before trusting a clean
result (stage a path, change it on the device, re-read without re-staging: frozen content
means you stage).

Steps 1–4 must happen in one continuous observer session; if it ends in between, the
cache dies with it and everything reads fresh.

  1. Device seat: create a small text file, e.g. probe.md, whose body contains a unique

marker line so any copy identifies itself:
``
VERSION: v1
MARKER: probe-v1-9bf861ce
``
Record its size and md5.

  1. Observer (staging session): stage/read probe.md. It returns v1 — expected; this

caches the path. Keep this session open.

  1. Device seat: change probe.md in place — VERSION: v2, a new marker. Record the new

size and md5. (Do this three ways across three files to characterise the key: one that
grows, one that shrinks, and one whose byte length is unchanged while content differs.)

  1. Same observer session: re-stage the same paths and compare, for each file:
  • the byte count/metadata the tool reports, versus
  • the bytes actually delivered (length, md5, marker line).

Observed: all three deliver the v1 marker; reported metadata matches the device's new
values. The same-size file is delivered stale with reported size == delivered size.

  1. Re-stage the grown file two or three more times, and again after ~15 minutes — still

stale, no self-heal.

  1. Create a new file that has never been staged and stage it — correct. Read the grown

file via a device-native execution path instead of staging — correct.

Minimal discriminator if you only run one probe: step 3's same-byte-length content change.
It is what proves the key excludes content, and it is the case no client-side size check can
catch.

Claude Model

Opus

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

Claude Desktop: 1.22209.3.0 (Claude_1.22209.3.0_x64__pzs8sxrjxfjjc); CLAUDE_AGENT_SDK_VERSION: 0.3.215; CLAUDE_CODE_ENTRYPOINT: claude-desktop

Platform

Other

Operating System

Windows

Terminal/Shell

Other

Additional Information

staging-cache-bug-report.md

staging-probe-RESULTS.md

staging-probe-LEDGER.md

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗