headroomlabs-ai/headroom · error · RuntimeError
`{' '.join(cmd)}` failed (exit {proc.returncode}): {stderr_b
Error message
`{' '.join(cmd)}` failed (exit {proc.returncode}):
{stderr_blob} What it means
Raised after proc.wait() in the learn analyzer's streaming CLI path when the child process (claude-cli or a litellm-backed CLI) exits with a non-zero return code. The message embeds the command line, the exit code, and the first _MAX_SNIPPET_LEN chars of collected stderr so the underlying CLI failure (auth, quota, bad model name) is visible.
Source
Thrown at headroom/learn/analyzer.py:761
eofs += 1
continue
last_activity = time.monotonic()
if tag == "stdout":
stdout_lines.append(line)
event = _parse_stream_event(line)
if event is not None and event.get("type") == "result":
# Last result event wins if multiple are emitted.
result_text = event.get("result")
if isinstance(result_text, str):
final_result = result_text
else:
stderr_lines.append(line)
proc.wait()
if proc.returncode != 0:
stderr_blob = "".join(stderr_lines)[:_MAX_SNIPPET_LEN]
raise RuntimeError(f"`{' '.join(cmd)}` failed (exit {proc.returncode}):\n{stderr_blob}")
stderr_blob = "".join(stderr_lines)
if stderr_blob.strip():
logger.debug("CLI stderr (exit 0): %s", stderr_blob[:_MAX_SNIPPET_LEN])
if final_result is None:
stdout_snippet = "".join(stdout_lines)[:_MAX_SNIPPET_LEN]
raise RuntimeError(
f"`{' '.join(cmd)}` did not emit a final `result` event. "
f"First {_MAX_SNIPPET_LEN} chars of stdout:\n{stdout_snippet}"
)
try:
return _strip_fenced_json(final_result)
except json.JSONDecodeError as exc:
snippet = final_result[:_MAX_SNIPPET_LEN]
raise RuntimeError(
f"`{' '.join(cmd)}` returned unparseable output. "View on GitHub (pinned to 322425c43b)
Solutions
- Read the embedded stderr snippet — it names the actual CLI failure (auth, quota, model name) and dictates the fix.
- If auth-related: run the CLI directly (e.g. `claude` once) to complete login/token refresh, then rerun `headroom learn`.
- If quota/rate-limit: wait and retry, or switch backend with --model <litellm-model-name>.
- Verify the CLI version is compatible; reinstall/update it if the error mentions unknown arguments.
- Reproduce manually with the exact command shown in the message to iterate faster.
Example fix
# before: opaque failure inside headroom learn headroom learn # RuntimeError: `claude --output-format stream-json ...` failed (exit 1): Invalid API key ... # after: fix the root cause the snippet names claude login # refresh credentials headroom learn
Defensive patterns
Strategy: try-catch
Validate before calling
import shutil
cli = "claude" # whatever backend headroom will invoke
if shutil.which(cli) is None:
raise SystemExit(f"{cli} not in PATH; run auth/install before `headroom learn`") Try / catch
try:
result = run_learn(...)
except RuntimeError as e:
msg = str(e)
if "failed (exit" in msg:
# stderr snippet is embedded after the newline; surface it to the operator
log.error("learn CLI failed: %s", msg.split("\n", 1)[-1])
if "auth" in msg.lower() or "api key" in msg.lower():
raise SystemExit("Re-authenticate the CLI, then rerun") from e
raise Prevention
- Smoke-test the backend CLI manually (one trivial invocation) before batch learn runs.
- Keep credentials fresh; schedule a periodic `claude` interactive check or token refresh.
- Log the full RuntimeError — the embedded stderr names the real failure.
When it happens
Trigger: Any claude-cli invocation inside `headroom learn` that exits non-zero: expired or missing credentials, invalid --model flag, API 4xx/5xx surfaced by the CLI, or the CLI crashing on a malformed prompt. Happens in both the plain run() path (analyzer.py:613) and the streaming path (analyzer.py:761).
Common situations: Claude OAuth token expired and `claude` needs re-login; API quota/ billing exhausted; user passed an unknown model via --model; CLI was updated and now rejects an argument headroom passes; rate limit at exit time.
Related errors
- `{' '.join(cmd)}` failed (exit {result.returncode}): {stderr
- `{' '.join(cmd)}` produced no output for {idle_cap}s. Check
- Error: {e}
- Shutting down...
- Proxy exited with code {proc.returncode}: {tail}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/065ce933744ed103.
Report an issue: GitHub.