Agent exit-1 with empty stderr fails the whole run — retry the agent and surface the real error (usage limit / structured-output retry cap)

Author: Blakeolson21Created Jul 6, 2026Updated Sep 21, 2026
Labelsbugready-for-pr

Environment: no-mistakes v1.33.0 (7bb3546), claude CLI 2.1.201 (Claude Code), macOS.

Symptom

When a claude pipeline agent exits 1, daemon.log records:

claude exited: exit status 1:

— with an empty error message, and the run fails immediately. The blank hides at least two distinct root causes, and in one of them the agent had already finished all of its real work.

Root cause A: Claude usage limit (session/weekly)

In -p/print mode the claude CLI writes the limit banner only to stdout / the transcript JSONL (as an isApiErrorMessage event), then exits 1 with empty stderr. Step log from run 01KWT4YYJGMSMQ4QF5F0N6ABHT (review.log):

reviewing changes...

You've hit your weekly limit · resets Jul 9 at 9pm (America/Chicago)
error: agent review: claude exited: exit status 1:

The real reason is sitting one line above the blank error, but the pipeline error carries none of it.

Root cause B: claude CLI structured-output retry cap

The agent completes all real work, then fails only at the final report-submission step: the CLI rejects a schema-mismatched StructuredOutput call up to 5 times, then exits 1 silently.

Run 01KWTVYRGNDYMWTMXAMWQTHK2J, test step: the agent had 103 tests green and screenshots captured. Its transcript shows exactly 5 StructuredOutput tool calls, every one rejected:

  • Attempts 1–4 used review-agent keys (findings, summary) in a TEST step: Output does not match required schema: root: must have required property 'tested', ... 'testing_summary', ... 'artifacts'
  • Attempt 5 supplied summary/tested/testing_summary/artifacts but was rejected for must have required property 'findings' — and its summary string contained XML junk (</summary>\n<findings">[]) from the agent trying to smuggle the findings key in.

After the 5th rejection: exit 1, empty stderr, run failed. Same pattern observed on runs 01KWTBG8GZ... (91 tests green) and 01KWTX6SKR.... Fully green work is discarded because of a reporting-path failure.

What the binary does today

From inspecting the v1.33.0 binary: the only agent retry is the transient-network backoff (%s retrying after transient error %q (attempt %d/%d), matching tokens like 429/503/529/overloaded_error/rate_limit_error/connection reset). A bare exit-1 with an empty message matches no token, so it is never retried. The auto_fix counters are a finding-fix loop and are never consulted on agent crashes. There is also no agent failover to fall back to (the ordered fallback list is unimplemented — #398; config rejects agent: [codex, claude]).

Suggestions

  1. Treat exit-1 with empty stderr as retryable. Re-spawn the same agent once or twice with backoff (config knob, e.g. agent_crash_retries) instead of failing the run. For case B a fresh attempt typically submits a correct report, since the underlying work is done.
  2. Surface the real error. When claude exits 1 blank, read the tail of the step's stdout / the last isApiErrorMessage in the session transcript and put that in the pipeline-failed error instead of an empty string. Usage-limit exits deserve distinct classification — park/resume, or a clear "wait until <reset time>" outcome instead of a generic failed (related: #400's session-limit handling ask).
  3. Don't discard completed work on a structured-output failure. The binary already has leniency strings (could not parse structured output, using text response). Extending that to the nonzero-exit-after-usable-output case — the agent's work and prose report are complete in the step log — would let the step succeed from the text report instead of losing green runs.

Related: #391 (same exit-1 surface, unescaped < in structured result — the attempt-5 XML junk above may share a root cause), #398 (no fallback agent to absorb these), #400 (session-limit handling / error classification), #154 (transient-retry precedent).