[BUG] Cowork: mount serves null bytes for rename target after recreating rename-source path — git unusable on mounted folders

Open 💬 2 comments Opened Jun 11, 2026 by Chrisibisi005

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 a file is created at a path that was just used as the source of a rename, the Cowork sandbox mount serves null bytes (correct size, all \x00) for the rename's target — via both read() and mmap. The cache appears to be path-keyed, not inode-keyed: recreating the rename-source path rebinds the renamed file's cached view to the new (empty) file.

Git's lockfile discipline (config.lock → rename → config, repeated per update) hits this sequence on every consecutive config/index/ref write, so git init fails deterministically on any mounted folder with fatal: bad config line 1 in file .../.git/config (the parser reads 36 null bytes). The file on the host disk is correct throughout — only the sandbox view is corrupted. Read-modify-write from the sandbox would propagate the corruption to host disk.

Distinction from known issues

  • #42520 (FUSE cache coherency on host-side writes): requires a host-side writer (MCP). Here all operations are sandbox-bash-originated, single session.
  • #40264 (truncation after external edit, cross-session): requires external modification and a session boundary. Neither involved here. (Its "cache keyed by filename" observation matches this defect's behaviour.)
  • #55877 (Edit/Write tools vs bash, rclone cache): requires the host-side file tools. No file tool is involved in this repro.

This is sandbox-only, single-session, deterministic, with a 3-step syscall repro.

Bisection results (each cycle: write lock → rename → condition → read target)

| Variant | Result |
| --- | --- |
| rename → read, nothing else | OK |
| rename → prior ENOENT probe of target | OK |
| rename → create unrelated file in same dir → read | OK |
| rename → recreate file at rename-source pathmmap target | NULLS (36/36) |
| rename → recreate file at rename-source path → read() target | NULLS (36/36) |
| same sequence on sandbox-native /tmp (control) | OK |

Reproduced 5/5 across repeated cycles. Also reproduced with git init directly: 3/3 failures on two different mounts (one Google Drive mirror, one plain local NTFS folder — defect is mount-stack-wide, not Drive-related).

strace excerpt (git init)

openat(AT_FDCWD, ".../.git/config", O_RDONLY) = -1 ENOENT
openat(AT_FDCWD, ".../.git/config.lock", O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3
rename(".../.git/config.lock", ".../.git/config") = 0
openat(AT_FDCWD, ".../.git/config.lock", O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC, 0666) = 3   <- rebind trigger
openat(AT_FDCWD, ".../.git/config", O_RDONLY) = 4                                      <- open succeeds, read returns nulls
=> fatal: bad config line 1 in file .../.git/config

A secondary symptom: after the failed init, ls .git/ listed config (size 54) while open()/stat of the same path returned ENOENT — directory-entry vs lookup divergence in the same cache layer.

Impact

  • git is unusable on mounted folders (init, and by extension every index/ref/config update).
  • Any tool using the lockfile-rename idiom (atomic-save editors, databases, package managers) risks silent null-byte reads and, via read-modify-write, real host-disk corruption.

Environment

  • Platform: Windows 11, Claude desktop app (Cowork mode)
  • Mount stack: NTFS → virtiofs → bindfs (FUSE) → sandbox
  • Reproduced on: Google Drive mirror folder AND plain local folder (identical behaviour)

What Should Happen?

Creating a file at a former rename-source path must not affect the cached view of the rename target. Cache entries should be keyed/invalidated by inode, not path. git init should succeed on a mounted workspace folder.

Error Messages/Logs

fatal: bad config line 1 in file /sessions/<session>/mnt/<folder>/.git/config

(no error from the filesystem itself — reads silently return null bytes; secondary symptom: ls lists .git/config while open()/stat return ENOENT)

Steps to Reproduce

Minimal reproduction (no git required) — run inside the sandbox on any mounted workspace folder:

import os

d = "/sessions/<session>/mnt/<folder>/repro"
os.makedirs(d, exist_ok=True)
target, lock = f"{d}/config", f"{d}/config.lock"
content = b"[core]\n\trepositoryformatversion = 0\n"

# 1. create lock and write content
fd = os.open(lock, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666)
os.write(fd, content); os.close(fd)

# 2. rename lock -> target (git's atomic-update pattern)
os.rename(lock, target)

# 3. create a NEW file at the rename-source path (git does this for the next update)
fd2 = os.open(lock, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0o666)

# 4. read target -> returns correct size but all null bytes
with open(target, "rb") as f:
    data = f.read()
print(len(data), data)   # 36  b'\x00\x00...'   EXPECTED: the [core] content
os.close(fd2)

Or simply: open a Cowork session with any folder mounted and run git init in it from bash — fails deterministically with fatal: bad config line 1.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

N/A — Cowork (Claude desktop app), sandbox environment

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

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