headroomlabs-ai/headroom · error · RuntimeError
`{' '.join(cmd)}` produced no output for {idle_cap}s. Check
Error message
`{' '.join(cmd)}` produced no output for {idle_cap}s. Check network connectivity, raise HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS, or try a different backend with --model <litellm-model-name>. What it means
Raised by the claude-cli streaming watchdog in headroom's learn analyzer when the subprocess produces no output line for idle_cap seconds (HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS). The process is killed and the partially-completed analysis is aborted; any LLM work in flight is lost. It exists so a hung network call or stalled backend cannot block the learn pipeline forever.
Source
Thrown at headroom/learn/analyzer.py:729
subprocess.TimeoutExpired
): # pragma: no cover — defensive, kill normally returns fast
pass
logger.debug("claude-cli killed: %s", reason)
while eofs < 2:
elapsed = time.monotonic() - start
if elapsed > hard_cap:
_kill(f"hard cap {hard_cap}s exceeded")
raise RuntimeError(
f"`{' '.join(cmd)}` exceeded the {hard_cap}s hard cap. "
"Raise HEADROOM_LEARN_CLI_TIMEOUT_SECS for slower networks or "
"larger digests, or try a different backend with "
"--model <litellm-model-name>."
)
idle_elapsed = time.monotonic() - last_activity
if idle_elapsed > idle_cap:
_kill(f"idle cap {idle_cap}s exceeded")
raise RuntimeError(
f"`{' '.join(cmd)}` produced no output for {idle_cap}s. "
"Check network connectivity, raise "
"HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS, or try a different "
"backend with --model <litellm-model-name>."
)
# Block up to 1s waiting for the next event, then re-check deadlines.
try:
tag, line = events.get(timeout=1.0)
except queue.Empty:
continue
if line is None:
eofs += 1
continue
last_activity = time.monotonic()
if tag == "stdout":
stdout_lines.append(line)View on GitHub (pinned to 322425c43b)
Solutions
- Raise the idle cap: export HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS=120 (or higher) and rerun.
- Check network connectivity to the backend (e.g. curl the Anthropic API endpoint) and retry once the link is stable.
- Switch backend entirely: rerun with --model <litellm-model-name> (e.g. --model gpt-4o) to bypass claude-cli streaming.
- Update the claude CLI (`claude update` / npm i -g @anthropic-ai/claude-code) — old versions buffer stream-json output instead of emitting per-line events.
- If it recurs, capture debug logs to see how far the stream got before stalling.
Example fix
# before headroom learn # dies with 'produced no output for 60s' # after export HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS=180 headroom learn
Defensive patterns
Strategy: retry
Validate before calling
import os, socket, urllib.parse
LIVE = 0 if os.environ.get("HEADROOM_DRY_RUN") else 1 # guard: allow opting out before a network-bound learn run
host = urllib.parse.urlparse(os.environ.get("HEADROOM_CLI_URL", "https://api.anthropic.com")).hostname
try:
socket.create_connection((host, 443), timeout=5).close()
network_ok = True
except OSError:
network_ok = False
if LIVE and not network_ok:
raise SystemExit("No route to backend; skipping learn run") Try / catch
try:
result = headroom_learn_run(...)
except RuntimeError as e:
if "produced no output for" in str(e) and "HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS" in str(e):
os.environ["HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS"] = "180"
result = headroom_learn_run(...) # one retry with a raised cap
else:
raise Prevention
- Set HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS to 2-3x your worst observed model time-to-first-token.
- Warm the backend (one tiny CLI call) before launching a large learn batch to surface auth/network issues cheaply.
- Run learn jobs on a stable network or behind a retrying proxy; avoid VPN switching mid-run.
- Keep claude-cli updated — old versions buffer stream-json and look identical to a hang.
When it happens
Trigger: Running `headroom learn` (or _call_claude_cli_streaming) with the default claude-cli backend when the model API stalls: no stdout/stderr line arrives for idle_cap seconds while the process is still alive. Typical on flaky networks, rate-limited or overloaded API endpoints, VPN drops, or a claude-cli version that buffers output.
Common situations: Slow hotel/coffee-shop Wi-Fi, corporate proxies that black-hole long-lived streams, claude-cli silently waiting on an expired OAuth token refresh, or a very large digest prompt where the model thinks longer than the idle cap before emitting the first token.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- `{' '.join(cmd)}` did not respond within {hard_cap}s. Check
- `{' '.join(cmd)}` failed (exit {proc.returncode}): {stderr_b
- upstream request failed: {0}
- Error: {e}
- No available port found in range {start_port}-{end_port - 1}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/c012e8e40c78b0d1.
Report an issue: GitHub.