anthropics / anthropics/claude-code
Bash tool kills every command after ~5s ("output file was replaced or could no longer be verified") when an EDR transiently hard-links new files
- 主要言語
- Python
- スター
- 145k
- フォーク
- 23.1k
- PR マージ指標
- PR 指標を取得中
説明
**Version:** 2.1.263 (also reproduced on 2.1.260). macOS 26.3 (Darwin 25.3.0), Apple Silicon. Genian Insights EDR installed (system extension).
## Symptom
Every Bash tool call, foreground or background, sandboxed or with `dangerouslyDisableSandbox`, dies about 5 seconds after start with exit code 137 and:
```
Command killed: its output file was replaced or could no longer be verified
//tasks/.output could not be read (unknown). This usually means another Claude Code process in the same project deleted it during startup cleanup.>
```
Short commands like `pwd` also fail: the command runs and the output file contains the correct output, but the read-back is reported as failed. No other Claude Code process is active in the project. The Read tool reads the same file fine.
## Root cause
The EDR on this machine adds a second hard link to every newly created file on the boot volume for roughly 1 to 8 seconds, then removes it. `stat` on the task output file shows `nlink=2` during that window, then `nlink=1`. Inode, size, owner, mode and content never change. Claude Code's output-file identity check (introduced in 2.1.251, "a sandboxed command cannot redirect or replace them") treats this state as a replaced file, sends SIGTERM then SIGKILL to the shell, and reports the output as unreadable with error code `unknown`.
## Evidence
- Polling `stat -f %l` on any new file (in /tmp, $HOME, ~/.claude, a 0700 dir) shows nlink 1 → 2 → 1 within ~8s. On a separately mounted APFS RAM disk or sparse image, nlink stays 1.
- A trap inside the shell recorded SIGTERM at exactly +5s from the parent `claude` process; the loop kept writing after that until SIGKILL.
- Headless reproduction with `claude -p "run: echo hi && sleep 7 && echo alive" --allowedTools 'Bash(echo:*)'`:
- default tmp dir: killed, 137
- `CLAUDE_CODE_TMPDIR=~/cctmp` (boot volume, 0700): killed, 137
- `CLAUDE_CODE_TMPDIR=/Volumes//t`: works
- `CLAUDE_CODE_TMPDIR=/Volumes//t`: works
## Expected
A transient extra hard link with unchanged dev/inode is not a replaced file. The check should compare device and inode (and possibly owner/mode) rather than failing on `nlink > 1`, or at least retry before killing the command. The error message blaming "another Claude Code process ... startup cleanup" is misleading here.
## Workaround
Point `CLAUDE_CODE_TMPDIR` at a directory on a separately mounted volume (settings.json `env` works).
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
調査の方向性
Reproduce with the provided headless claude -p command while polling stat -f %l on the output file, comparing the default boot-volume tmp directory with the APFS RAM disk and sparse image cases. Trace the Bash tool's output-file identity check introduced in 2.1.251 and verify that unchanged device/inode metadata survives a transient extra hard link without killing the command or misreporting the output.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- bash, macos
- 領域
- cli, operating-systems, security
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 55/100