Skip to content

fix(server): Claude turns say Claude Code exited when it dies at startup - #26242

Open
juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-16297
Open

juliusmarminge wants to merge 3 commits into
mainfrom
fix/issue-16297

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

When the Claude Code CLI exits before it sends its first message, the turn fails about 10 ms after prompt.offer with only Provider turn failed. (transport_error, no code). The cause is not recorded anywhere. One example is macOS denying the CLI access to the project folder, where it prints error: An internal error occurred (EPERM) and exits 1. T3 starts the CLI with stderr set to ignore, so the CLI's own explanation is thrown away. The server log then shows only "Claude Code process exited with code 1".

To reproduce on Linux, point a Claude instance at a CLI that writes that line to stderr and exits 1, then send any prompt. The turn fails with Provider turn failed. and the EPERM line appears nowhere.

Change

  • stderr is kept: spawnClaudeProcessGroup now pipes the CLI's stderr and always drains it. The query runner keeps up to 2,048 characters of it until the CLI's first message, the same limit the SDK uses when it starts the CLI itself.
  • Startup exits get a named error: a nonzero exit or a signal before the first message becomes a ClaudeCodeStartupExitError. failure.ts lists it as a curated error, so the turn reads "Claude Code exited with code 1 before it responded. Check the server log for details."
  • stderr stays in the server log: the stderr tail is attached only to the error's cause chain. The existing orchestration-v2.claude-query-stream-failed server warning prints it. It never reaches the failure message clients see, because stderr can carry paths or tokens.
  • Unchanged cases: a stop (SDK AbortError), exit code 0, a spawn that fails before any exit, and a CLI that dies after it has answered all keep today's error. The Windows spawn path feeds the same tail.

Scope and approval

Fixes #16297

Triaged via the automated issue flow; GitCafe issue #16297.

Verification

Tests

  • ClaudeAgentSdkQueryRunner.test.ts gains two tests that use the real query runner and a fake CLI. That CLI writes a private-looking EPERM line to stderr and exits 1.
    • names Claude Code's exit code when it exits before answering: the failure message includes "exited" and "code 1" and not the stderr text.
    • keeps the CLI's startup stderr for the server log: Cause.pretty includes the stderr line.
  • Both fail on main (expected 'Provider turn failed.' to include 'exited'; stderr missing from the cause) and pass with the fix.
  • The query-runner and failure.test.ts suites pass: 22 tests.
  • vp run check:changed passed at the merged head: format, lint, knip, typecheck of 23 packages, and 266 tests in 3 files.

Runtime
This is a server-only fix with no render path, so there are no captures. I ran a real dev server on a fresh database, t3 thread start --wait with the reporter's prompt, and a stub CLI:

Before (main 36d4d711b7) After (0004ae9b87)
Turn failure Provider turn failed. Claude Code exited with code 1 before it responded. Check the server log for details.
claude-query-stream-failed cause Claude Code process exited with code 1 ClaudeCodeStartupExitError → stderr: error: An internal error occurred (EPERM) → SDK exit error
Real Claude CLI 2.1.303, same prompt OK OK

Runtime transcript · stub CLI

Not checked

  • cannot-verify macos: I did not run an actual macOS folder-access denial. The stub reproduces its observable shape (stderr line, exit 1, no SDK frames).
  • Windows was not executed; its spawn path only adds the same tail.

Models: Claude Opus 5.5 and GPT-6.1-Sol; harnesses: Claude Agent SDK and Codex through T3 Code.

🤖 Generated with Claude Code

juliusmarminge and others added 3 commits October 27, 2026 15:58
…xit and stderr

A Claude CLI that writes to stderr and exits 1 before its first message
fails the turn with only "Provider turn failed.", and the stream failure
cause carries no trace of the CLI's stderr for the server log. These
tests reproduce #16297 and fail on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Claude CLI that exited before its first message (for example macOS
denying it the project folder with EPERM) failed the turn with only
"Provider turn failed.", and its stderr was discarded, so the server log
had no reason either.

The process-group spawn now pipes and always drains stderr, keeping a
2048-char tail until the first message arrives. A query that fails before
answering, after a nonzero exit or a signal, and is not an SDK abort,
becomes a curated ClaudeCodeStartupExitError ("Claude Code exited with
code 1 before it responded."). The stderr tail rides only in its cause
chain, so Cause.pretty in the stream-failure warning shows it while the
client message never does. Windows' own spawn feeds the same tail.

Fixes #16297

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/provider-core/src/server/failure.ts
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 28, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 40f3260

Macroscope's review found this PR approvable — Claude Code startup failures now show an exit code or signal, with a bounded stderr tail available through server logs rather than the user-facing message. The change is limited to failure diagnostics and preserves normal-response and explicit-stop handling.

You can add or adjust custom eligibility rules. Learn more.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB +6 B (+0.1%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.3 KiB +6 B (+0.5%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.6 KiB 21.6 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 2 2 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB −29 B (−0.6%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.3 KiB 1.2 KiB −29 B (−2.3%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB −41 B (−0.2%) 24.4 KiB ✅
Claude Live turn messages 2 1 −1 (−50.0%) 8 ✅

Baseline: 7a059b9 · PR result: 40f3260 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 7a059b9 and 40f3260.

Files selected for processing: 3
  • apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
  • apps/server/src/orchestration-v2/Adapters/ClaudeAgentSdkQueryRunner.test.ts
  • packages/provider-core/src/server/failure.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Walkthrough

The Claude runner now captures CLI stderr and exit status before the first response. For qualifying early exits, it creates a startup error that includes the exit information and retains stderr in the cause. The provider failure mapper recognizes the startup error message.

Changes: Claude startup error handling

Layer / File(s) Summary
Capture CLI stderr and exit status
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
The process-group spawn pipes stderr to an optional callback. The runner retains up to 2,048 characters of stderr until the first response and records the process exit code and signal.
Convert early exits to provider errors
apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts, packages/provider-core/src/server/failure.ts, apps/server/src/orchestration-v2/Adapters/ClaudeAgentSdkQueryRunner.test.ts
For qualifying stream failures, the runner creates a startup error and attaches captured stderr to its cause. The provider failure mapper recognizes the error message. Tests check the reported exit code and retained stderr.

Priority: Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeCLI
  participant ClaudeAgentSdkQueryRunner
  participant curatedCauseMessages
  ClaudeCLI->>ClaudeAgentSdkQueryRunner: stderr and exit status before first response
  ClaudeAgentSdkQueryRunner->>curatedCauseMessages: ClaudeCodeStartupExitError
  curatedCauseMessages-->>ClaudeAgentSdkQueryRunner: known failure message
Loading

Possibly related issues

  • pingdotgg/t3code#26082 — Both changes concern early Claude process exits that otherwise produce a generic client error; this change reports exit details and retains stderr.

Suggested reviewers: t3dotgg

Merge Risk: Low · up to 40f32

Early exits now report an exit code, but server logs may occasionally omit part of the startup diagnostic. This is a bounded diagnostic risk.

Pre-merge checks: Passed 3 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Linked Issues check Warning Direct issue #16297 requires a Claude turn launched from T3 to behave like the same authenticated CLI and return OK. The PR changes only failure reporting. ClaudeCodeStartupExitError still fails a… Fix the startup condition that causes the macOS T3 launch to exit before the first message, so the affected authenticated CLI completes the turn and returns OK. Add automated coverage for the relevant startup path and successful first res…
Passed Passed checks: 3 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: reporting a named error when Claude Code exits during startup.
Description check Passed The description includes complete Problem, Change, Scope and approval, and Verification sections. It documents behavior, tests, runtime checks, limitations, linked issue, and agent details.
Out of Scope Changes check Passed The changes stay within issue #16297. Stderr capture, bounded retention, startup-exit classification, server-log cause details, failure-message curation, and regression tests all address diagnosis or …
Full details: Out of Scope Changes check

Explanation

The changes stay within issue #16297. Stderr capture, bounded retention, startup-exit classification, server-log cause details, failure-message curation, and regression tests all address diagnosis or handling of the reported early Claude turn failure. No unrelated product area or behavior is changed.

Full details: Linked Issues check

Explanation

Direct issue #16297 requires a Claude turn launched from T3 to behave like the same authenticated CLI and return OK. The PR changes only failure reporting. ClaudeCodeStartupExitError still fails a nonzero exit before the first message, and the added tests assert that failure. The PR description states that an actual macOS access-denial run was not verified and does not show that this condition now returns OK.

Resolution

Fix the startup condition that causes the macOS T3 launch to exit before the first message, so the affected authenticated CLI completes the turn and returns OK. Add automated coverage for the relevant startup path and successful first response.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

GigaZoro pushed a commit to GigaZoro/CodeShip that referenced this pull request Oct 30, 2026
…tup (upstream pingdotgg#26242)

Cherry-picked from pingdotgg#26242

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Claude turns fail immediately with transport_error on macOS while standalone CLI and non-TTY subprocess succeed

1 participant