fix(docker): resolve Claude binary to glibc variant on Debian image#1521
fix(docker): resolve Claude binary to glibc variant on Debian image#1521
Conversation
Bun's hoisted linker installs both glibc and musl optional-dep packages for the detected CPU arch. The SDK's resolver picks musl first, which fails to execute on the Debian (glibc) base image — the musl dynamic loader is absent, causing every Claude call to fail. Remove the stale ENV CLAUDE_BIN_PATH pointing to the SDK 0.1.x cli.js path (no longer present in SDK 0.2.x), and add runtime arch detection in docker-entrypoint.sh. The entrypoint maps uname -m output to the correct glibc package suffix (x86_64→linux-x64, aarch64→linux-arm64) and exports CLAUDE_BIN_PATH before exec-ing the server. Users can still override via CLAUDE_BIN_PATH in their .env or docker run -e. Closes #1519 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughRemoved the build-time Changes
Sequence Diagram(s)sequenceDiagram
participant Docker as Container runtime
participant Entrypoint as docker-entrypoint.sh
participant FS as Filesystem (binaries)
participant Bun as Bun (setup/start)
Docker->>Entrypoint: start container
Entrypoint->>Entrypoint: check CLAUDE_BIN_PATH env
alt CLAUDE_BIN_PATH not set
Entrypoint->>Entrypoint: run `uname -m` -> arch
Entrypoint->>FS: check candidate binary for arch
alt candidate exists & executable
Entrypoint->>Entrypoint: export CLAUDE_BIN_PATH
else candidate missing/non-exec
Entrypoint->>Docker: write error and exit
end
else CLAUDE_BIN_PATH set
Entrypoint->>Entrypoint: keep provided CLAUDE_BIN_PATH
end
Entrypoint->>Bun: bun run setup-auth
Bun->>Bun: setup completes
Entrypoint->>Bun: bun run start
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docker-entrypoint.sh`:
- Around line 45-49: The case block that sets CLAUDE_BIN_PATH based on uname -m
needs a default branch to fail fast on unsupported architectures and validate
the chosen binary exists: add a *) default in the case for docker-entrypoint.sh
that prints an explicit error mentioning the detected "$(uname -m)" and exits
non-zero, and after setting CLAUDE_BIN_PATH (in the x86_64/aarch64 branches) add
a test like [ -x "$CLAUDE_BIN_PATH" ] or [ -f "$CLAUDE_BIN_PATH" ] || { echo
"error: missing or non-executable $CLAUDE_BIN_PATH" >&2; exit 1; } so the script
errors clearly if the binary is absent or the architecture is unsupported.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f1a814ae-6b15-4662-ad8b-52f73df51e1e
📒 Files selected for processing (2)
Dockerfiledocker-entrypoint.sh
Add a *) fallback branch so operators on unsupported architectures (riscv64, ppc64le, etc.) get an explicit warning at startup rather than a silent no-op followed by a cryptic SDK error later. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…BIN_PATH Add a file-existence check after the uname -m arch detection so a missing or renamed binary in a future SDK version emits a clear WARN at startup rather than letting the container start cleanly and failing silently on first Claude invocation. Follows the project's Fail Fast principle: surface the problem as early as possible with an actionable message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
docker-entrypoint.sh (1)
49-56:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFail fast here instead of warning-and-continuing.
Line 49 and Line 55 currently continue startup after warning, which reintroduces delayed runtime failure; and Line 52 checks
-f(exists) instead of-x(executable). Exit non-zero in these cases so startup fails clearly.Proposed hardening patch
if [ -z "${CLAUDE_BIN_PATH:-}" ]; then case "$(uname -m)" in - x86_64) _CLAUDE_BIN_CANDIDATE="/app/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64/claude" ;; - aarch64) _CLAUDE_BIN_CANDIDATE="/app/node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64/claude" ;; - *) echo "WARN: Unsupported CPU architecture $(uname -m). Set CLAUDE_BIN_PATH manually if Claude fails to start." >&2 ;; + x86_64|amd64) _CLAUDE_BIN_CANDIDATE="/app/node_modules/@anthropic-ai/claude-agent-sdk-linux-x64/claude" ;; + aarch64|arm64) _CLAUDE_BIN_CANDIDATE="/app/node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64/claude" ;; + *) + echo "ERROR: Unsupported CPU architecture $(uname -m). Set CLAUDE_BIN_PATH manually." >&2 + exit 1 + ;; esac if [ -n "${_CLAUDE_BIN_CANDIDATE:-}" ]; then - if [ -f "$_CLAUDE_BIN_CANDIDATE" ]; then + if [ -x "$_CLAUDE_BIN_CANDIDATE" ]; then export CLAUDE_BIN_PATH="$_CLAUDE_BIN_CANDIDATE" else - echo "WARN: Pinned Claude binary not found at ${_CLAUDE_BIN_CANDIDATE}. SDK will use its default resolver — Claude may fail to start." >&2 + echo "ERROR: Claude binary missing or non-executable at ${_CLAUDE_BIN_CANDIDATE}" >&2 + exit 1 fi fi fi#!/bin/bash set -euo pipefail file="docker-entrypoint.sh" echo "Claude pinning block:" nl -ba "$file" | sed -n '45,58p' echo echo "Within this block, verify unsupported-arch handling and exit behavior:" awk 'NR>=45 && NR<=58 && /Unsupported CPU architecture|exit [0-9]+/' "$file" echo echo "Within this block, verify candidate check uses executable bit:" awk 'NR>=45 && NR<=58 && /\[ -[fx] /' "$file"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docker-entrypoint.sh` around lines 49 - 56, The startup currently warns and continues on unsupported CPU or missing/non-executable pinned binary; change the behavior in the CLAUDE pinning block so that when the unsupported-architecture case is hit it exits with non-zero, and when _CLAUDE_BIN_CANDIDATE is set but either missing or not executable use [ -x "$_CLAUDE_BIN_CANDIDATE" ] and exit non-zero with a clear error message instead of only warning; ensure CLAUDE_BIN_PATH is only exported when the candidate exists and is executable (symbol references: _CLAUDE_BIN_CANDIDATE, CLAUDE_BIN_PATH and the Unsupported CPU architecture case).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@docker-entrypoint.sh`:
- Around line 49-56: The startup currently warns and continues on unsupported
CPU or missing/non-executable pinned binary; change the behavior in the CLAUDE
pinning block so that when the unsupported-architecture case is hit it exits
with non-zero, and when _CLAUDE_BIN_CANDIDATE is set but either missing or not
executable use [ -x "$_CLAUDE_BIN_CANDIDATE" ] and exit non-zero with a clear
error message instead of only warning; ensure CLAUDE_BIN_PATH is only exported
when the candidate exists and is executable (symbol references:
_CLAUDE_BIN_CANDIDATE, CLAUDE_BIN_PATH and the Unsupported CPU architecture
case).
The arch-detection block warned and continued in two failure modes — unsupported CPU and missing pinned binary — which left CLAUDE_BIN_PATH unset and silently fell through to the SDK's musl-first resolver, the exact bug this fix targets. Exit non-zero in both cases so startup surfaces a clear error instead of a delayed runtime failure. Also switch the existence check from -f to -x (the next thing we do with the path is execute it) and unset the helper var so it doesn't leak into the child process environment. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
CLAUDE_BIN_PATHpointed to the SDK 0.1.xcli.jspath which no longer exists in SDK 0.2.x; and even without that env var, the SDK's resolver picks the musl binary first, which cannot execute on the Debian glibc base image.dev— Claude is the default provider and is completely non-functional.ENV CLAUDE_BIN_PATH=.../cli.jsfromDockerfile; added runtime CPU arch detection indocker-entrypoint.shthat pinsCLAUDE_BIN_PATHto the correct glibc package (linux-x64for x86_64,linux-arm64for aarch64) before starting the server.binary-resolver.tsalready handlesCLAUDE_BIN_PATHcorrectly. Users can still override via.envordocker run -e CLAUDE_BIN_PATH=....UX Journey
Before
After
Architecture Diagram
Before
After
Connection inventory:
DockerfileCLAUDE_BIN_PATHdocker-entrypoint.shCLAUDE_BIN_PATHenv varbinary-resolver.tsCLAUDE_BIN_PATHLabel Snapshot
risk: lowsize: XScidocker:entrypointChange Metadata
bugciLinked Issue
Validation Evidence (required)
No TypeScript/JS code changed — only
Dockerfile(comment replacement) anddocker-entrypoint.sh(shell script). Type-check, lint, format, and test suites are unaffected. Docker build validation requires a live Docker daemon.The fix follows the same pattern described in issue #1519 and verified by the issue author on linux/arm64. This PR extends that fix to handle x86_64→x64 arch mapping correctly (the issue's suggested
ARG TARGETARCHapproach would have silently produced a wrong path on amd64 builds).claude.binary_resolvedlog showssource=envSecurity Impact (required)
Compatibility / Migration
CLAUDE_BIN_PATHin.envare unaffected (the entrypoint skips the auto-detection whenCLAUDE_BIN_PATHis already set).envfiles work unchangedHuman Verification (required)
What was personally validated beyond CI:
binary-resolver.ts— env var is read at step 1, beforeBUNDLED_IS_BINARYcheck, so it works in both source and binary modeCLAUDE_BIN_PATHin.envis preserved; unknown arch (e.g. riscv64) silently skips the auto-detection, falling through to SDK's default resolutionSide Effects / Blast Radius (required)
binary-resolver.tslogsclaude.binary_resolvedwithsource=envand the resolved path — visible in container logsRollback Plan (required)
CLAUDE_BIN_PATHmanually in.envas a workaroundCLAUDE_BIN_PATHin.envat any timeorchestrator_message_failed/title.generate_failedin container logs;CLAUDE_BIN_PATH is set to "..." but the file does not existerrorRisks and Mitigations
binary-resolver.tsfile-existence check will surface a clear error immediately if the path becomes stale; users can override viaCLAUDE_BIN_PATHSummary by CodeRabbit
Bug Fixes
Documentation