fix: remove .claude.json file bind mount regression#911
Conversation
File bind mounts on Linux prevent atomic writes (temp file + rename() returns EBUSY). Claude Code uses atomic writes for ~/.claude.json config, causing silent failure with 0 tool calls. The writable home volume already provides a writable $HOME, and entrypoint.sh creates .claude.json from CLAUDE_CODE_API_KEY_HELPER env var. Fixes github/gh-aw#16214 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
📰 DEVELOPING STORY: Smoke Copilot reports was cancelled. Our correspondents are investigating the incident... |
|
Chroot tests failed Smoke Chroot was cancelled - See logs for details. |
|
💫 TO BE CONTINUED... Smoke Claude was cancelled! Our hero faces unexpected challenges... |
|
🌑 The shadows whisper... Smoke Codex was cancelled. The oracle requires further meditation... |
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (1 files)
Coverage comparison generated by |
Mossaka
left a comment
There was a problem hiding this comment.
Code Review
Summary
This PR correctly removes the .claude.json file bind mount that was causing Claude Code to fail on atomic writes (rename() returns EBUSY on Linux file bind mounts). The fix is clean and minimal.
Analysis
Correctness:
- The writable home volume (
${workDir}-chroot-home:/host${effectiveHome}:rw) atdocker-manager.ts:525already provides a writable$HOMEdirectory. Files created within it (including.claude.json) support normal filesystem operations including atomic writes. entrypoint.shlines 126-163 correctly handle all three cases: file exists with matchingapiKeyHelper, file exists withoutapiKeyHelper, and file doesn't exist. The "file doesn't exist" path (lines 157-161) is the one that will now be taken since docker-manager no longer pre-creates the file.- No side effects — the bind mount was redundant with the writable home volume, and the entrypoint already had the creation logic.
Comment quality:
- The replacement comment (lines 552-555) is clear and well-written. It documents both the what (no bind mount) and the why (atomic writes), plus points to the alternative mechanism.
One minor nit (non-blocking):
entrypoint.shline 150 still says"overwrites empty {} created by docker-manager". Since docker-manager no longer creates this file, the comment is now stale. This is a cosmetic issue and doesn't affect correctness — the code path still works fine if a file happens to exist withoutapiKeyHelperfor any other reason.
Verdict
The change is correct, well-documented, and addresses the root cause. The entrypoint handles all edge cases properly. LGTM.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verification Report: PR #911 — Remove .claude.json file bind mountIssue Summary (#16214)AWF v0.18.0 introduced a regression where Fix AnalysisThe change: Removes the file bind mount of Root cause addressed: YES. The file bind mount ( Verification Checklist1. Atomic writes will work after the fix
2. entrypoint.sh still creates .claude.json correctly
3. API proxy is unaffected
4. One-shot token handling is unaffected
5. No other code references the removed bind mount
ConclusionThe fix correctly addresses issue #16214. The file bind mount was the root cause of EBUSY/EACCES errors. After removal, This aligns with the project's documented guidance in CLAUDE.md:
|
There was a problem hiding this comment.
Pull request overview
Removes the host file bind-mount for ~/.claude.json in generateDockerCompose to avoid Linux file bind-mount semantics that can break Claude Code’s atomic config writes, relying instead on the existing writable home volume and entrypoint initialization.
Changes:
- Deleted host-side creation of
~/.claude.jsonand its corresponding file bind-mount into/host. - Added an inline note documenting why
~/.claude.jsonmust not be mounted as a file.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // The writable home volume provides a writable $HOME, and entrypoint.sh | ||
| // creates ~/.claude.json with apiKeyHelper content from CLAUDE_CODE_API_KEY_HELPER. |
There was a problem hiding this comment.
The comment implies entrypoint.sh always creates ~/.claude.json, but that only happens when CLAUDE_CODE_API_KEY_HELPER is set. Consider rewording to make that conditional explicit (and/or mention that otherwise Claude Code will create/manage the file itself on the writable home volume).
| // The writable home volume provides a writable $HOME, and entrypoint.sh | |
| // creates ~/.claude.json with apiKeyHelper content from CLAUDE_CODE_API_KEY_HELPER. | |
| // The writable home volume provides a writable $HOME. When CLAUDE_CODE_API_KEY_HELPER | |
| // is set, entrypoint.sh creates ~/.claude.json with apiKeyHelper content; otherwise | |
| // Claude Code itself will create and manage ~/.claude.json on the writable home volume. |
| // NOTE: ~/.claude.json is NOT bind-mounted as a file. File bind mounts on Linux | ||
| // prevent atomic writes (temp file + rename), which Claude Code requires. | ||
| // The writable home volume provides a writable $HOME, and entrypoint.sh | ||
| // creates ~/.claude.json with apiKeyHelper content from CLAUDE_CODE_API_KEY_HELPER. |
There was a problem hiding this comment.
This change removes a previously mounted volume, but there isn’t a unit test asserting that ~/.claude.json is not present as a file bind-mount in the generated agent volumes. Since docker-manager.test.ts already asserts other chroot volume mounts, add a regression test to ensure the volumes list does not contain an entry ending in ':/host$HOME/.claude.json:rw' (or the equivalent resolved path).
|
Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded. |
|
📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤 |
|
🎬 THE END — Smoke Claude MISSION: ACCOMPLISHED! The hero saves the day! ✨ |
|
✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟 |
Deno Build Test Results
Overall: ✅ PASS All Deno tests completed successfully.
|
C++ Build Test Results
Overall: PASS ✅ All C++ projects built successfully.
|
Smoke Test Results (Copilot)Last 2 Merged PRs:
Test Results:
Status: PASS cc @Mossaka
|
Go Build Test Results
Overall: PASS ✅ All Go projects successfully downloaded dependencies and passed tests.
|
Node.js Build Test Results ✅All Node.js projects tested successfully!
Overall: PASS ✅ All npm install and test commands completed successfully without errors.
|
.NET Build Test Results
Overall: PASS ✅ All .NET projects successfully restored, built, and executed.
|
Smoke Test Results - Claude EngineLast 2 Merged PRs:
Test Results:
Status: PASS
|
Build Test: Bun ✅
Overall: PASS All Bun build tests completed successfully.
|
Rust Build Test Results ✅
Overall: PASS All Rust projects built and tested successfully.
|
|
Merged PRs: perf: parallelize container image builds in release workflow | feat: add ARM64 multi-architecture container builds
|
Chroot Version Comparison Test Results
Overall Status: ❌ Tests Failed The chroot environment does not have matching versions for Python and Node.js. Go versions match correctly.
|
Summary
.claude.jsonfile bind mount added in v0.18.0 that prevents Claude Code from writing its configrename()returnsEBUSY), breaking Claude Code which uses temp file + rename$HOME, andentrypoint.shalready creates.claude.jsonfromCLAUDE_CODE_API_KEY_HELPERRoot Cause
v0.18.0 added code at
src/docker-manager.ts:552-568that:~/.claude.jsonon the host (writes{})${claudeJsonPath}:/host${claudeJsonPath}:rwThis is unnecessary because:
${config.workDir}-chroot-home:/host${effectiveHome}:rw) already provides a writable$HOMEentrypoint.sh(lines 126-163) already creates.claude.jsonwithapiKeyHelpercontent fromCLAUDE_CODE_API_KEY_HELPERenv varWhat Changed
Removed the entire
.claude.jsonbind mount block (17 lines deleted):fs.writeFileSync(claudeJsonPath, '{}'))agentVolumes.push(...))Added an explanatory comment (4 lines) documenting why this mount is intentionally omitted.
No other files changed —
entrypoint.shalready handles the "file does not exist" case correctly.Test plan
npm run build— TypeScript compiles cleanlynpm test— 794 tests pass, 0 failuresnpm run lint— 0 errors (274 pre-existing warnings).claude.jsonon writable volume (not bind mount).claude.jsonFixes github/gh-aw#16214
🤖 Generated with Claude Code