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

  1. Raise the idle cap: export HEADROOM_LEARN_CLI_IDLE_TIMEOUT_SECS=120 (or higher) and rerun.
  2. Check network connectivity to the backend (e.g. curl the Anthropic API endpoint) and retry once the link is stable.
  3. Switch backend entirely: rerun with --model <litellm-model-name> (e.g. --model gpt-4o) to bypass claude-cli streaming.
  4. 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.
  5. 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

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

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/c012e8e40c78b0d1. Report an issue: GitHub.