anthropics / anthropics/claude-code
Sandboxed Bash wedges permanently when the working directory is on a read-only filesystem (no cwd recovery)
- Vorherrschende Sprache
- Python
- Sterne
- 145k
- Forks
- 23.1k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
### Preflight Checklist
- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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?
When the sandboxed Bash tool's working directory ends up on a read-only filesystem (e.g. a read-only bind mount the agent `cd`s into), **every subsequent Bash command fails during sandbox setup, before the command runs**, and the session cannot recover on its own.
Because setup fails before the command executes, even a `cd` back to a writable directory never runs — the shell is sealed out of its own recovery. Read/Edit/Glob/Grep still work (they don't spawn a sandboxed shell), so only the Bash tool dies. A single `cd` into a read-only mount kills Bash for the rest of the session.
**Root cause.** For every Bash command, sandbox setup prepares two things **under the shell's current working directory**, before bwrap launches:
1. a `.claude/settings.json` write-mask (a bind rule) — bwrap must *create* the mount-point file for it, which fails on a read-only fs;
2. an atomic-write staging dir `/.claude/.cc-writes`, created with a plain `mkdir`, which also fails on a read-only fs.
Both run on **every** invocation (a bare `pwd` triggers them), keyed on the cwd persisted from the *previous* command — so once the cwd is a read-only mount, the next command's setup targets `/.claude/…` and aborts. The intent ("prepare to write next to the working directory") is meaningless on a filesystem nothing can write to, but setup treats the failure as fatal.
### What Should Happen?
A read-only working directory should make the per-command scratch/mask setup a no-op (nothing can be written there anyway) and Bash commands should keep working — or the session should recover the cwd to a writable path, exactly as it already does when the working directory has been *deleted* (it logs `Shell CWD "…" no longer exists, recovering to "…"` and resets the tracked cwd).
### Error Messages/Logs
```shell
bwrap: Can't create file at /.claude/settings.json: Read-only file system
bwrap: Can't mkdir /.claude: Read-only file system
```
### Steps to Reproduce
Minimal, no specific project or model. Any Linux host with the CLI:
1. Create a read-only directory the agent can `cd` into:
```
mkdir -p /tmp/ro && echo hello > /tmp/ro/file.txt
sudo mount --bind /tmp/ro /tmp/ro
sudo mount -o remount,ro,bind /tmp/ro
# (or in a container, mount a volume read-only: -v /tmp/ro:/work/ro:ro)
```
2. In a session with the Bash sandbox enabled, run each as a separate Bash tool call:
```
cd /tmp/ro && pwd # OK -> /tmp/ro
ls # OK (first command after the cd)
pwd # FAILS -> bwrap: Can't mkdir /tmp/ro/.claude: Read-only file system
cd /tmp && pwd # FAILS identically -- the escape never runs
```
From the 3rd command on, every command (including `echo`, `true`, or the `cd` that would fix it) fails at sandbox setup.
**Isolating the mechanism (no CLI needed)** — the two failing setup operations, reproduced directly with bubblewrap:
```
bwrap --dev-bind / / --ro-bind /tmp/ro /tmp/ro -- sh -c 'cd /tmp/ro && pwd'
# -> /tmp/ro (exit 0; denying the mount as a whole is fine)
bwrap --dev-bind / / --ro-bind /dev/null /tmp/ro/.claude/settings.json -- true
# -> bwrap: Can't create file at /tmp/ro/.claude/settings.json: Read-only file system (exit 1)
```
### Claude Model
_No response_
### Is this a regression?
No, this never worked
### Last Working Version
_No response_
### Claude Code Version
Reproduced on 2.1.226 and 2.1.229; present on 2.1.235. bubblewrap 0.11.0. Independent of model and API platform.
### Platform
Other
### Operating System
Other Linux
### Terminal/Shell
Non-interactive/CI environment
### Additional Information
**The asymmetry that points at a small fix:** the tool already recovers when the working directory has been *deleted* (ENOENT → resets the tracked cwd to a writable fallback, `Shell CWD "…" no longer exists, recovering to "…"`), but not when the cwd is *read-only* (EROFS on the staging setup). Either fix is sufficient:
1. **Tolerate `EROFS`/`EACCES`** when pre-creating the per-command staging dir (`.claude/.cc-writes`) and the settings write-mask under the cwd — a read-only fs cannot host a planted settings file or a staging dir, so skipping them there is safe.
2. **Extend the existing deleted-cwd recovery** to also reset the cwd when it is read-only.
**Related** (same "sandbox setup must write under a read-only path" class, other angles): #50781 (`Can't create file at .zshrc` kills all Bash), #46560 (cwd is `~/.claude`), #25603 (denyWithinAllow artifacts in the working dir), #43096 (`/tmp/claude` TMPDIR chicken-and-egg). This report is the **general case**: any read-only mount reached by a normal `cd`, with the deleted-cwd-recovers / read-only-cwd-wedges asymmetry.
Beitragsleitfaden
Für dieses Repository ist kein Beitragsleitfaden indexiert
Rechercherichtung
Reproduce the failure on Linux with the Bash sandbox and bubblewrap commands from the report, using a read-only mounted working directory. Trace the per-command sandbox setup for the settings write-mask and .claude/.cc-writes staging directory, then compare it with the existing deleted-cwd recovery; done means Bash commands continue working or the cwd recovers after entering a read-only mount.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- bash, linux, python
- Bereich
- cli, operating-systems, security
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 55/100