Cowork sandbox (virtiofs bind-mount) silently drops git unlink() with EPERM, leaving stale tmp_obj_* files and .lock files on host

Resolved 💬 2 comments Opened May 26, 2026 by Beardy-Man Closed Jun 27, 2026

Environment

  • macOS host (Darwin 25.5.0), repos bind-mounted into Linux sandbox via com.apple.Virtualization.VirtualMachine.xpc
  • Sandbox uid (wonderful-magical-hawking) differs from host owner uid
  • Affects all repos Cowork touches — reproduced across 5 separate repos in one studio

Symptom

After a Cowork session, .git/objects/*/tmp_obj_* files accumulate on the host filesystem. Stale .git/index.lock and .git/HEAD.lock files also persist after session-kill, blocking host-side git operations with:

fatal: Another git process seems to be running in this repository, e.g.
an editor opened by 'git commit'. Please make sure all processes are
terminated then try again.

Inside the sandbox, git operations sometimes fail with unlink: Operation not permitted on .git/objects/*/tmp_obj_* files, confirming the sandbox cannot delete files it created.

Root cause (diagnosed)

Git's atomic object write sequence:

  1. open(tmp_obj, O_CREAT|O_WRONLY) → succeeds
  2. write compressed object data → succeeds
  3. fchmod(fd, 0444) → succeeds
  4. link(tmp_obj, <sha1_hash>) → succeeds (hard-link count → 2; named object exists)
  5. unlink(tmp_obj)FAILS with EPERM

The named object is written correctly (confirmed by hard-link count = 2 on every orphaned file). Only the cleanup of the tmp name fails. The com.apple.Virtualization.VirtualMachine.xpc sandbox policy permits open(O_CREAT) and link() on bind-mounted host paths but not unlink(). The same mechanism leaves in-progress lock files behind when a session VM is killed mid-operation.

Evidence

  • 81 orphaned tmp_obj_* files found across 5 repos after normal Cowork usage (oldest 6 days, all nlink=2)
  • 6 stale .lock files across 4 repos (oldest 27 days)
  • lsof confirms only com.apple.Virtualization.VirtualMachine (PID varies per session) holds open handles into .git/
  • No macOS immutable flags (chflags) or blocking extended attributes on any of the affected files
  • ls -laO on .git/ shows - flags throughout; only com.apple.provenance xattr on .git/index (harmless)
# Sample: named object and stranded tmp name are the same inode
.git/objects/3b/
  -r--r--r--  2  tonypurkins  7fb24114514b5d7c324bc88eca2f0f79694f26   ← named object
  -r--r--r--  2  tonypurkins  tmp_obj_IRVfqf                            ← stranded tmp, same inode

Workaround deployed (user-side)

Host-side launchd agent running every 300 s removes tmp_obj_* files older than 300 s with nlink ≥ 2, and .lock files older than 300 s. This is a host-side patch for a sandbox-side bug.

Suggested fix

The virtiofs implementation in com.apple.Virtualization.VirtualMachine.xpc should permit unlink() on files created by the guest process in a bind-mounted host directory. Alternatively, Cowork could run a post-session cleanup hook on the host that removes any tmp_obj_* and .lock files left behind in bind-mounted repos.

Reported by

Cowork user Beardy-Man (Data Argo studio). Diagnosed with Claude Code (claude-sonnet-4-6) on 2026-05-26.

View original on GitHub ↗

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