{"record":{"id":"e98fcde4e15f2718","repo":"headroomlabs-ai/headroom","slug":"headroom-proxy-exited-with-code-self-process-ret","errorCode":null,"errorMessage":"headroom proxy exited with code {self._process.returncode}","messagePattern":"headroom proxy exited with code (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"headroom/testing/harness.py","lineNumber":1499,"sourceCode":"        if process is not None and process.poll() is None:\n            process.terminate()\n            try:\n                process.wait(timeout=5)\n            except subprocess.TimeoutExpired:\n                process.kill()\n                process.wait(timeout=5)\n        if self._log_file is not None:\n            self._log_file.close()\n            self._log_file = None\n\n    def _wait_ready(self) -> None:\n        assert self._process is not None\n        url = f\"http://127.0.0.1:{self.port}{self.ready_path}\"\n        deadline = time.monotonic() + self.timeout_s\n        last_error: str | None = None\n        while time.monotonic() < deadline:\n            if self._process.poll() is not None:\n                raise RuntimeError(f\"headroom proxy exited with code {self._process.returncode}\")\n            try:\n                with urllib.request.urlopen(url, timeout=0.5) as response:\n                    if response.status == 200:\n                        return\n            except (OSError, urllib.error.URLError) as exc:\n                last_error = str(exc)\n            time.sleep(0.25)\n        raise TimeoutError(f\"headroom proxy was not ready at {url}: {last_error}\")\n\n\nclass _NoopClient:\n    \"\"\"Minimal original-client object for SDK simulations without upstream I/O.\"\"\"\n\n\nclass ScenarioOrchestrator:\n    \"\"\"Runs built scenarios against local no-key tasks and evaluates guarantees.\"\"\"\n\n    def __init__(","sourceCodeStart":1481,"sourceCodeEnd":1517,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/testing/harness.py#L1481-L1517","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the proxy log file the harness captured (it closes _log_file on stop) for the traceback/exit reason.","Reproduce the spawn command manually (scenario.proxy_command() builds it) and run it in your shell to see stderr.","If the port is taken (returncode != 0 right after bind), free the port or configure proxy_config.port to another value.","Verify required env vars/credentials and that the headroom CLI version matches the harness."],"exampleFix":"# before\nproxy = HeadroomProxy(command=..., port=8080)\nproxy.start()  # RuntimeError: exited with code 1\n\n# after\nimport subprocess, tempfile\nlog = tempfile.NamedTemporaryFile(delete=False)\nproc = subprocess.Popen(command, stdout=log, stderr=subprocess.STDOUT)\nproc.wait(); print(open(log.name).read())  # inspect crash, fix cause, then restart","handlingStrategy":"fallback","validationCode":"import socket\nwith socket.socket() as s:\n    s.bind((\"127.0.0.1\", port))  # raises if port taken; pick another before starting","typeGuard":null,"tryCatchPattern":"try:\n    proxy.start()\nexcept RuntimeError as e:\n    if \"exited with code\" in str(e):\n        log = proxy._log_path or \"proxy.log\"\n        raise RuntimeError(f\"proxy crashed at startup; see {log}: {e}\") from e\n    raise","preventionTips":["Always capture the proxy log file path and surface it on failure.","Smoke-test the proxy command manually when flags/env change.","Ensure credentials and the headroom CLI version match the harness.","Free or randomize the port between runs."],"tags":["process","proxy","startup","subprocess","ci"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}