Skip to content

Self-host CLI reviews report opaque claude_code_exit_1 when the real error is in stdout #1612

Description

@JSONbored

Problem

claude --output-format json reports API / auth / model errors in its stdout JSON envelope ({is_error:true, api_error_status:404, result:"…"}) — on a non-zero exit too, with empty stderr. In createClaudeCodeAi.run the order is:

if (code !== 0) throw new Error(`claude_code_exit_${code}: ${redactSecrets(stderr…)}`);  // wins first
const errStatus = claudeErrorStatus(stdout);                                              // never reached on exit≠0
if (errStatus) throw new Error(`claude_code_error_${errStatus}`);

So when claude exits non-zero with the error in stdout, the exit-code throw wins and emits claude_code_exit_1: (blank — stderr is empty), discarding the precise status. This is exactly why #1610 (a claude --model claude-code 404) was undiagnosable from logs/Sentry and required deep in-container reproduction to root-cause. #1605 added stderr capture, but stderr is empty for this whole class of failure.

Fix

Check claudeErrorStatus(stdout) before the exit-code throw, so a structured error surfaces as claude_code_error_404 (precise, bounded, secret-free) instead of an opaque claude_code_exit_1: . A genuine crash (non-zero exit with no structured stdout envelope) still falls through to the exit code + redacted stderr. Regression test pins the non-zero-exit + stdout-envelope case.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions