headroomlabs-ai/headroom · error · RuntimeError

`{' '.join(cmd)}` exceeded the {hard_cap}s hard cap. Raise H

Error message

`{' '.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>.

What it means

Raised in the streaming claude-cli watchdog loop: every iteration checks elapsed = time.monotonic() - start against hard_cap, and once the total runtime exceeds it, the child process is killed via _kill() and this RuntimeError is raised. Unlike the idle cap (last-activity based, handled separately), the hard cap bounds TOTAL wall-clock time regardless of streaming progress; default 300s via HEADROOM_LEARN_CLI_TIMEOUT_SECS.

Source

Thrown at headroom/learn/analyzer.py:720

    stderr_lines: list[str] = []
    final_result: str | None = None
    eofs = 0

    def _kill(reason: str) -> None:
        proc.kill()
        try:
            proc.wait(timeout=5)
        except (
            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)

View on GitHub (pinned to 322425c43b)

Solutions

  1. Raise the total cap: export HEADROOM_LEARN_CLI_TIMEOUT_SECS=900 (or more)
  2. Reduce digest size / analyze shorter sessions if the run is legitimately huge
  3. Diagnose whether the CLI is actually progressing (watch its stream events in debug logs) — if hung, fix network/auth rather than raising the cap
  4. Switch to an API backend which has no CLI watchdog: headroom learn --model <litellm-model-name>

Example fix

# before
headroom learn  # streaming claude-cli killed: hard cap 300s exceeded

# after
export HEADROOM_LEARN_CLI_TIMEOUT_SECS=1200
headroom learn
Defensive patterns

Strategy: retry

Validate before calling

import os
default_cap = 300
if int(os.environ.get('HEADROOM_LEARN_CLI_TIMEOUT_SECS', default_cap)) < estimated_run_secs:
    os.environ['HEADROOM_LEARN_CLI_TIMEOUT_SECS'] = str(estimated_run_secs * 2)

Try / catch

try:
    recs = run_learn()
except RuntimeError as e:
    if 'exceeded the' in str(e) and 'hard cap' in str(e):
        os.environ['HEADROOM_LEARN_CLI_TIMEOUT_SECS'] = str(int(os.environ.get('HEADROOM_LEARN_CLI_TIMEOUT_SECS', '300')) * 2)
        recs = run_learn()
    else:
        raise

Prevention

When it happens

Trigger: `headroom learn` streaming a large digest through claude-cli that legitimately needs more than the cap (or hangs while still trickling output — the idle cap won't fire if lines keep arriving, so the hard cap is the last resort).

Common situations: Very large session digests with default 300s; slow networks/proxies; a previously-set low HEADROOM_LEARN_CLI_TIMEOUT_SECS lingering in the environment; the CLI rate-limited but emitting keep-alive events that defeat the idle watchdog.

Understand the failure class

Related errors


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