headroomlabs-ai/headroom · critical · RuntimeError

headroom proxy exited with code {self._process.returncode}

Error message

headroom proxy exited with code {self._process.returncode}

What it means

Raised while waiting for the headroom proxy subprocess to become healthy: the readiness poll in _wait_ready() noticed the child process had already terminated (poll() returned a non-None exit code). The message includes the process return code; the proxy's log file (opened by the harness) usually contains the real crash reason.

Source

Thrown at headroom/testing/harness.py:1499

        if process is not None and process.poll() is None:
            process.terminate()
            try:
                process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                process.kill()
                process.wait(timeout=5)
        if self._log_file is not None:
            self._log_file.close()
            self._log_file = None

    def _wait_ready(self) -> None:
        assert self._process is not None
        url = f"http://127.0.0.1:{self.port}{self.ready_path}"
        deadline = time.monotonic() + self.timeout_s
        last_error: str | None = None
        while time.monotonic() < deadline:
            if self._process.poll() is not None:
                raise RuntimeError(f"headroom proxy exited with code {self._process.returncode}")
            try:
                with urllib.request.urlopen(url, timeout=0.5) as response:
                    if response.status == 200:
                        return
            except (OSError, urllib.error.URLError) as exc:
                last_error = str(exc)
            time.sleep(0.25)
        raise TimeoutError(f"headroom proxy was not ready at {url}: {last_error}")


class _NoopClient:
    """Minimal original-client object for SDK simulations without upstream I/O."""


class ScenarioOrchestrator:
    """Runs built scenarios against local no-key tasks and evaluates guarantees."""

    def __init__(

View on GitHub (pinned to 322425c43b)

Solutions

  1. Read the proxy log file the harness captured (it closes _log_file on stop) for the traceback/exit reason.
  2. Reproduce the spawn command manually (scenario.proxy_command() builds it) and run it in your shell to see stderr.
  3. If the port is taken (returncode != 0 right after bind), free the port or configure proxy_config.port to another value.
  4. Verify required env vars/credentials and that the headroom CLI version matches the harness.

Example fix

# before
proxy = HeadroomProxy(command=..., port=8080)
proxy.start()  # RuntimeError: exited with code 1

# after
import subprocess, tempfile
log = tempfile.NamedTemporaryFile(delete=False)
proc = subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT)
proc.wait(); print(open(log.name).read())  # inspect crash, fix cause, then restart
Defensive patterns

Strategy: fallback

Validate before calling

import socket
with socket.socket() as s:
    s.bind(("127.0.0.1", port))  # raises if port taken; pick another before starting

Try / catch

try:
    proxy.start()
except RuntimeError as e:
    if "exited with code" in str(e):
        log = proxy._log_path or "proxy.log"
        raise RuntimeError(f"proxy crashed at startup; see {log}: {e}") from e
    raise

Prevention

When it happens

Trigger: Starting the proxy process via the harness with a bad flag, missing dependency, or missing provider credentials so it exits during startup; port already in use causing immediate exit; incompatible Python/interpreter used to spawn the proxy.

Common situations: CI runners without ANTHROPIC_API_KEY/OPENAI_API_KEY; stale port binding from a previous crashed run; upgrading headroom where CLI flags changed while the harness pins old ones; the headroom package not importable in the spawned interpreter.

Related errors


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