Skip to content

claude adapter swallows the agent's real error on non-zero exit - reports empty "claude exited with code 1: " for API/model errors #157

Description

@ooiyeefei

What happened?

Expected vs actual:

▎ Expected: when the spawned claude CLI fails with an API/model error, gnhf surfaces that error in notes.md and the iteration record so I can diagnose it.

▎ Actual: gnhf reports only claude exited with code 1: (note the empty string after the colon). The real, actionable error was emitted by claude on stdout as a stream-json result event, but gnhf builds its failure message from stderr only, which was empty - so the diagnostic is discarded. This makes the failure undiagnosable and silently triggers the consecutive-failure + exponential-backoff path (1m → 2m → 4m of "waiting"). It took reading the raw .gnhf/runs/<id>/iteration-N.jsonl by hand to find the actual cause.

Two concrete cases I hit (same opaque symptom, different real cause):

▎ - On AWS Bedrock, a bare model id → stdout result: {"is_error":true,"api_error_status":400,"result":"API Error (claude-opus-4-8): 400 The provided model identifier is invalid.. Try --model to switch to us.anthropic.claude-opus-4-7.","error":"model_not_found"}
▎ - On the first-party subscription, a us.anthropic.-prefixed id → stdout result: {"is_error":true,"api_error_status":404,"result":"There's an issue with the selected model (us.anthropic.claude-opus-4-8[1m]). It may not exist or you may not have access to it. Run --model to pick a different model.","error":"model_not_found"}
▎ In both, gnhf logged only claude exited with code 1: .

Root cause (pinpointed in source):

src/core/agents/claude.ts, in child.on("close", (code) => …): on code !== 0 it does const detail = \ claude exited with code ${code}: ${stderr}and rejects with that. The adapter already parses the JSONL stream (it tracks resultEvent/finalStructuredResultEvent/latestResultUsage) but ignores the captured resultevent's result/is_error/api_error_status text on the error branch. Claude Code writes API errors to stdout, not stderr, sostderr` is empty.

Suggested fix:

▎ On non-zero exit, if a result event was captured, include its result string (and api_error_status / the last assistant text block) in the rejection message and the notes.md entry, falling back to stderr only when no result event exists.

How to reproduce

  • Command: gnhf --agent claude (resuming a run; no --model, so the spawned claude uses its saved settings.json default model)
  • Agent/model: claude; Claude Code routed to AWS Bedrock (CLAUDE_CODE_USE_BEDROCK=1) whose account/region didn't have the saved default model id → 400. Same opaque symptom reproduces on the first-party API with a Bedrock-format id → 404.
  • Anything that makes the spawned claude exit 1 after emitting a stdout result event with is_error:true reproduces it.

gnhf version

0.1.41

Agent

claude

Model / provider (if relevant)

claudecode , opus 4.8

OS and Node version

node --version: v22.16.0 , OS: Ubuntu 24.04.4 LTS · Linux 6.17.0-1017-aws x86_64

Debug log (gnhf.log)

{"timestamp":"2026-06-26T00:30:44.572Z","pid":879750,"event":"iteration:end","iteration":29,"elapsedMs":1363928,"success":true,"summary":"Confirmed the safe client-side anti-flash levers are exhausted and shipped the safe, verifiable half of lever #4's cold-start sign-in fold: businesses.getTrialStatusByClerkId now additively returns the user's saved language at zero extra DB read, enabling a future middleware fold to collapse two sequential cold-start Convex round-trips into one.","keyChanges":3,"keyLearnings":4,"consecutiveFailures":0,"totalInputTokens":10225052,"totalOutputTokens":93534,"tokensEstimated":false,"commitCount":22}
{"timestamp":"2026-06-26T00:30:44.579Z","pid":879750,"event":"iteration:start","iteration":30,"promptLength":2308,"consecutiveFailures":0,"totalInputTokens":10225052,"totalOutputTokens":93534,"git":{"head":"5e8b75cf61ec9ea849716a47e1b39cb2e00390f8","branch":"gnhf/improve-our-app-perf-2a865d","commitCount":22}}
{"timestamp":"2026-06-26T00:30:44.579Z","pid":879750,"event":"agent:run:start","iteration":30,"agent":"claude","logPath":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d/iteration-30.jsonl"}
{"timestamp":"2026-06-26T00:30:47.947Z","pid":879750,"event":"agent:run:error","iteration":30,"elapsedMs":3366,"error":{"name":"Error","message":"claude exited with code 1: ","stack":"Error: claude exited with code 1: \n    at ChildProcess.<anonymous> (file:///home/fei/.nvm/versions/node/v22.16.0/lib/node_modules/gnhf/dist/cli.mjs:14181:128)\n    at ChildProcess.emit (node:events:530:35)\n    at maybeClose (node:internal/child_process:1101:16)\n    at ChildProcess._handle.onexit (node:internal/child_process:304:5)"}}
{"timestamp":"2026-06-26T00:30:47.973Z","pid":879750,"event":"iteration:end","iteration":30,"elapsedMs":3394,"success":false,"summary":"claude exited with code 1: ","keyChanges":0,"keyLearnings":0,"consecutiveFailures":1,"totalInputTokens":10225052,"totalOutputTokens":93534,"tokensEstimated":false,"commitCount":22}
{"timestamp":"2026-06-26T00:30:47.973Z","pid":879750,"event":"backoff:start","iteration":30,"consecutiveErrors":1,"backoffMs":60000}
{"timestamp":"2026-06-26T00:31:47.974Z","pid":879750,"event":"backoff:end","iteration":30,"stopRequested":false}
{"timestamp":"2026-06-26T00:31:47.983Z","pid":879750,"event":"iteration:start","iteration":31,"promptLength":2308,"consecutiveFailures":1,"totalInputTokens":10225052,"totalOutputTokens":93534,"git":{"head":"5e8b75cf61ec9ea849716a47e1b39cb2e00390f8","branch":"gnhf/improve-our-app-perf-2a865d","commitCount":22}}
{"timestamp":"2026-06-26T00:31:47.983Z","pid":879750,"event":"agent:run:start","iteration":31,"agent":"claude","logPath":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d/iteration-31.jsonl"}
{"timestamp":"2026-06-26T00:31:51.082Z","pid":879750,"event":"agent:run:error","iteration":31,"elapsedMs":3099,"error":{"name":"Error","message":"claude exited with code 1: ","stack":"Error: claude exited with code 1: \n    at ChildProcess.<anonymous> (file:///home/fei/.nvm/versions/node/v22.16.0/lib/node_modules/gnhf/dist/cli.mjs:14181:128)\n    at ChildProcess.emit (node:events:530:35)\n    at maybeClose (node:internal/child_process:1101:16)\n    at ChildProcess._handle.onexit (node:internal/child_process:304:5)"}}
{"timestamp":"2026-06-26T00:31:51.107Z","pid":879750,"event":"iteration:end","iteration":31,"elapsedMs":3124,"success":false,"summary":"claude exited with code 1: ","keyChanges":0,"keyLearnings":0,"consecutiveFailures":2,"totalInputTokens":10225052,"totalOutputTokens":93534,"tokensEstimated":false,"commitCount":22}
{"timestamp":"2026-06-26T00:31:51.107Z","pid":879750,"event":"backoff:start","iteration":31,"consecutiveErrors":2,"backoffMs":120000}
{"timestamp":"2026-06-26T00:33:51.107Z","pid":879750,"event":"backoff:end","iteration":31,"stopRequested":false}
{"timestamp":"2026-06-26T00:33:51.116Z","pid":879750,"event":"iteration:start","iteration":32,"promptLength":2308,"consecutiveFailures":2,"totalInputTokens":10225052,"totalOutputTokens":93534,"git":{"head":"5e8b75cf61ec9ea849716a47e1b39cb2e00390f8","branch":"gnhf/improve-our-app-perf-2a865d","commitCount":22}}
{"timestamp":"2026-06-26T00:33:51.116Z","pid":879750,"event":"agent:run:start","iteration":32,"agent":"claude","logPath":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d/iteration-32.jsonl"}
{"timestamp":"2026-06-26T00:33:54.209Z","pid":879750,"event":"agent:run:error","iteration":32,"elapsedMs":3092,"error":{"name":"Error","message":"claude exited with code 1: ","stack":"Error: claude exited with code 1: \n    at ChildProcess.<anonymous> (file:///home/fei/.nvm/versions/node/v22.16.0/lib/node_modules/gnhf/dist/cli.mjs:14181:128)\n    at ChildProcess.emit (node:events:530:35)\n    at maybeClose (node:internal/child_process:1101:16)\n    at ChildProcess._handle.onexit (node:internal/child_process:304:5)"}}
{"timestamp":"2026-06-26T00:33:54.233Z","pid":879750,"event":"iteration:end","iteration":32,"elapsedMs":3117,"success":false,"summary":"claude exited with code 1: ","keyChanges":0,"keyLearnings":0,"consecutiveFailures":3,"totalInputTokens":10225052,"totalOutputTokens":93534,"tokensEstimated":false,"commitCount":22}
{"timestamp":"2026-06-26T00:33:54.233Z","pid":879750,"event":"orchestrator:abort","reason":"3 consecutive failures","iteration":32,"consecutiveFailures":3}
{"timestamp":"2026-06-26T00:33:54.234Z","pid":879750,"event":"orchestrator:end","status":"aborted","iterations":32,"successCount":1,"failCount":3,"totalInputTokens":10225052,"totalOutputTokens":93534,"commitCount":22}
{"timestamp":"2026-06-26T01:39:11.171Z","pid":879750,"event":"signal:SIGINT"}
{"timestamp":"2026-06-26T01:39:11.204Z","pid":879750,"event":"run:complete","signal":"SIGINT","status":"aborted","iterations":32,"successCount":1,"failCount":3,"totalInputTokens":10225052,"totalOutputTokens":93534,"commitCount":22,"worktreePath":null}
{"timestamp":"2026-06-26T01:46:35.855Z","pid":1367104,"event":"sleep:unavailable","command":"systemd-inhibit","exitCode":1}
{"timestamp":"2026-06-26T01:46:35.870Z","pid":1367104,"event":"run:start","args":["--agent","claude"],"runId":"improve-our-app-perf-2a865d","runDir":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d","agent":"claude","promptLength":607,"promptFromStdin":false,"startIteration":32,"preventSleep":true,"agentArgsOverride":["--model","us.anthropic.claude-opus-4-8"],"worktree":false,"worktreePath":null,"currentBranch":false,"push":false,"platform":"linux","nodeVersion":"v22.16.0","gnhfVersion":"0.1.41"}
{"timestamp":"2026-06-26T01:46:35.879Z","pid":1367104,"event":"orchestrator:start","agent":"claude","runId":"improve-our-app-perf-2a865d","startIteration":32,"push":false,"maxConsecutiveFailures":3,"baseCommit":"0ae5bb234596eef0487bc63d263a128576e50b74","initialCommitCount":22}
{"timestamp":"2026-06-26T01:46:35.885Z","pid":1367104,"event":"iteration:start","iteration":33,"promptLength":2713,"consecutiveFailures":0,"totalInputTokens":0,"totalOutputTokens":0,"git":{"head":"5e8b75cf61ec9ea849716a47e1b39cb2e00390f8","branch":"gnhf/improve-our-app-perf-2a865d","commitCount":22}}
{"timestamp":"2026-06-26T01:46:35.885Z","pid":1367104,"event":"agent:run:start","iteration":33,"agent":"claude","logPath":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d/iteration-33.jsonl"}
{"timestamp":"2026-06-26T02:07:00.764Z","pid":1367104,"event":"orchestrator:graceful-stop-requested","iteration":33,"hasActiveIteration":true,"status":"running"}
{"timestamp":"2026-06-26T02:07:00.764Z","pid":1367104,"event":"signal:SIGINT"}
{"timestamp":"2026-06-26T02:13:12.948Z","pid":1367104,"event":"agent:run:end","iteration":33,"elapsedMs":1597063,"success":true,"inputTokens":12685131,"outputTokens":102488,"cacheReadTokens":12643228,"cacheCreationTokens":223351,"estimated":false}
{"timestamp":"2026-06-26T02:13:12.982Z","pid":1367104,"event":"iteration:end","iteration":33,"elapsedMs":1597097,"success":true,"summary":"Eliminated a redundant second sequential cold-start Convex round-trip on the highest-traffic admin landing page (dashboard) — it now reads finance_admin from the role_permissions ensureUserProfile already returned instead of awaiting a separate getUserRole() call, shortening how long the loading.tsx skeleton lingers on first paint.","keyChanges":4,"keyLearnings":4,"consecutiveFailures":0,"totalInputTokens":12685131,"totalOutputTokens":102488,"tokensEstimated":false,"commitCount":23}
{"timestamp":"2026-06-26T02:13:12.982Z","pid":1367104,"event":"orchestrator:graceful-stop-complete","iteration":33,"consecutiveFailures":0}
{"timestamp":"2026-06-26T02:13:12.982Z","pid":1367104,"event":"orchestrator:end","status":"stopped","iterations":33,"successCount":1,"failCount":0,"totalInputTokens":12685131,"totalOutputTokens":102488,"commitCount":23}
{"timestamp":"2026-06-26T02:13:13.012Z","pid":1367104,"event":"run:complete","signal":"SIGINT","status":"stopped","iterations":33,"successCount":1,"failCount":0,"totalInputTokens":12685131,"totalOutputTokens":102488,"commitCount":23,"worktreePath":null}
{"timestamp":"2026-06-26T02:13:35.037Z","pid":1534288,"event":"sleep:unavailable","command":"systemd-inhibit","exitCode":1}
{"timestamp":"2026-06-26T02:13:35.052Z","pid":1534288,"event":"run:start","args":["--agent","claude","--max-iterations","35"],"runId":"improve-our-app-perf-2a865d","runDir":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d","agent":"claude","promptLength":607,"promptFromStdin":true,"startIteration":33,"maxIterations":35,"preventSleep":true,"agentArgsOverride":["--model","us.anthropic.claude-opus-4-8"],"worktree":false,"worktreePath":null,"currentBranch":false,"push":false,"platform":"linux","nodeVersion":"v22.16.0","gnhfVersion":"0.1.41"}
{"timestamp":"2026-06-26T02:13:35.060Z","pid":1534288,"event":"orchestrator:start","agent":"claude","runId":"improve-our-app-perf-2a865d","startIteration":33,"maxIterations":35,"push":false,"maxConsecutiveFailures":3,"baseCommit":"0ae5bb234596eef0487bc63d263a128576e50b74","initialCommitCount":23}
{"timestamp":"2026-06-26T02:13:35.066Z","pid":1534288,"event":"iteration:start","iteration":34,"promptLength":2713,"consecutiveFailures":0,"totalInputTokens":0,"totalOutputTokens":0,"git":{"head":"22e0454094020083cf134d04cc36ca7be469e93c","branch":"gnhf/improve-our-app-perf-2a865d","commitCount":23}}
{"timestamp":"2026-06-26T02:13:35.066Z","pid":1534288,"event":"agent:run:start","iteration":34,"agent":"claude","logPath":"/home/fei/fei/code/groot-finance/app-loading/.gnhf/runs/improve-our-app-perf-2a865d/iteration-34.jsonl"}

notes.md (optional)

Anything else

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions