anthropics / anthropics/claude-code

sandbox.enabled leaves $TMPDIR read-only when CLAUDE_CODE_TMPDIR is set

オープン
#92,590 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:sandbox bug has repro platform:linux
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

# `sandbox.enabled` leaves `$TMPDIR` read-only when `CLAUDE_CODE_TMPDIR` is set

**Version:** Claude Code 2.1.263
**Platform:** Linux (Debian trixie), bubblewrap 0.12.0
**Settings:** `"sandbox": { "enabled": true, "failIfUnavailable": true }`

## Summary

When `CLAUDE_CODE_TMPDIR` points at a directory under `$HOME`, a sandboxed
session sets `$TMPDIR` to that directory and then denies writes to it. Anything
using `mktemp` fails.

The sandbox's own guidance to the model says the opposite:

> For temporary files, always use the `$TMPDIR` environment variable. TMPDIR is
> automatically set to the correct sandbox-writable directory in sandbox mode.

## Reproduce

1. `export CLAUDE_CODE_TMPDIR="$HOME/.local/state/scratch"` (any directory under
`$HOME` outside the workspace)
2. Add to settings: `"sandbox": { "enabled": true, "failIfUnavailable": true }`
3. Start a session and run `mktemp -d`

Observed:

```
mktemp: failed to create directory via template
'/home//.local/state/scratch/tmp.XXXXXX': Read-only file system
```

`echo $TMPDIR` inside the session prints that same directory.

## Probable cause

The sandbox's reported write allowlist contains the literal token `"$TMPDIR"`
alongside fully expanded absolute paths:

```
"write": { "allowOnly": [ "/dev/stdout", ..., "/home//.npm/_logs",
".", "$TMPDIR" ] }
```

That is consistent with the variable never being expanded, and therefore never
matching the path it is supposed to permit.

## Knock-on effects

- Any tooling that calls `mktemp` fails. In my case an entire shell test suite
went red, with the failure surfacing as unrelated errors once `$TMP` was empty
and paths collapsed to `/`.
- **Nested sessions cannot start.** A child `claude` process cannot bind its
control socket in that directory:

```
Sandbox is required but failed to initialize: EPERM: operation not permitted,
listen '/home//.local/state/scratch/srt-mux-6-1.sock'. Restart to retry.
```

With `failIfUnavailable: true` it correctly refuses to run rather than
continuing unsandboxed — that part works exactly as documented.
- `gh` fails with `HTTP 401` in the same session, which I did not chase further.

## Suggested fix

Expand `$TMPDIR` when building the write allowlist, or resolve the sandbox's
temp root independently of `CLAUDE_CODE_TMPDIR` and set `$TMPDIR` to whatever
that resolves to. Either makes the documented contract true.

## Note on recovery

Worth knowing for anyone who hits this: the setting cannot be reverted from
inside a session, because writing the updated settings file requires writing
under `~/.claude`, which the sandbox denies. Restarting does not help either —
the live settings still enable it. The revert has to be made from a shell outside
Claude Code.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

Start with the Linux reproduction using CLAUDE_CODE_TMPDIR, sandbox.enabled, and mktemp, then trace the sandbox setup that builds the write allowlist. Done means the configured temporary directory is writable in a sandboxed session and nested sessions can create their sockets without weakening failIfUnavailable behavior.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
linux, python
領域
operating-systems, security
issue の種類
バグ
難易度
4/5
見積もり時間
3〜5日
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
50/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。