Bash tool: ~/.claude is bind-mounted read-only with no way to opt out, so hook state and config-manager writes always fail with EROFS
Summary
On Linux, the Bash tool runs in a mount namespace where ~/.claude, ~/.claude.json
and each trusted repo's .claude/ are bind-mounted read-only. Every write from Bash
into those paths therefore fails with EROFS, and there is no supported way to opt out:
"sandbox": { "enabled": false }in~/.claude/settings.jsondoes not remove it.- The Bash tool's
dangerouslyDisableSandbox: truedoes not remove it.
Meanwhile the Write and Edit tools write to the same paths without any trouble, so this
is not a policy boundary — it only blocks one of the two tool paths. That asymmetry is what
makes it costly: the restriction is invisible until a command fails, and the failure surfaces
as a bare EROFS with no mention of a mount.
Evidence
Claude Code 2.1.236, native build, Linux 5.15 (Ubuntu), no managed settings.
From inside a Bash tool call:
$ grep -i claude /proc/self/mountinfo
... /home/USER/.claude/debug /home/USER/.claude/debug rw,nosuid,nodev,relatime ...
... /home/USER/.claude/ide /home/USER/.claude/ide rw,nosuid,nodev,relatime ... tmpfs
... /home/USER/.claude /home/USER/.claude ro,nosuid,nodev,relatime ...
... /home/USER/.claude.json /home/USER/.claude.json ro,nosuid,nodev,relatime ...
... /home/USER/<repo>/.claude /home/USER/<repo>/.claude ro,nosuid,nodev,relatime ...
~/.claude/debug and ~/.claude/ide are rw submounts; everything else under ~/.claude is ro.
Writing there fails identically with and without the sandbox override:
$ chezmoi apply ~/.claude/settings.json
chezmoi: .claude/settings.json: open /home/USER/.claude/.settings.jsonNNNNNNNN: read-only file system
# exit 1 — same result with dangerouslyDisableSandbox: true
The same one-line change applied through the Edit tool succeeded on the first try, and the
file on disk reflected it.
Impact
- Hooks that persist their own state cannot be invoked from Bash. A
PreToolUsegate that
records "this was reviewed / this ticket was transitioned" under ~/.claude/<tool>/state/
works when the hook itself runs (Claude Code's own process), but the companion CLI that the
agent is supposed to run to write the marker fails every time. Users hit this as a hard
wall with no hint about the cause.
chezmoi apply(and any config manager) can never target~/.claudefrom Bash, so
declaratively-managed Claude configs need an out-of-band manual step on every change.
- Auto mode actively steers into it. While auto mode is active the agent is instructed to
prefer sed/heredocs over the Write and Edit tools for file changes — precisely the path
that cannot work for these paths. The result is a repeating try → EROFS → switch-to-Edit
round trip, several times per session.
What would help (any one of these)
- Document the mount and expose a supported, writable location for hook/CLI state (an
XDG_STATE_HOME-style path, or making ~/.claude/state an rw submount like debug/ide).
- Honor the existing escape hatches: either
sandbox.enabled: falseor
dangerouslyDisableSandbox: true lifting the config-dir bind mount would be unsurprising,
given both already read as "turn this protection off".
- At minimum, fail with a message that names the cause — e.g. "
~/.claudeis mounted
read-only for Bash; use the Write/Edit tools" — instead of a raw EROFS that gives the agent
nothing to act on.
Related but distinct: #78162 is the same error text from one-hop symlink resolution during
Claude Code's own atomic settings write, not from the Bash mount namespace.