headroomlabs-ai/headroom · info · SystemExit
Shutting down...
Error message
Shutting down...
What it means
The proxy CLI runs uvicorn via run_server(config, **run_kwargs) inside a try/except KeyboardInterrupt. On Ctrl-C (SIGINT) it prints 'Shutting down...', exits with code 130 (the conventional 128+SIGINT), and stops the embed watchdog in a finally block. This is the normal, graceful shutdown path for a foreground proxy — exit 130 is expected, not a crash.
Source
Thrown at headroom/cli/proxy.py:1659
"Falling back to per-worker embedder.",
err=True,
)
os.environ.pop("HEADROOM_EMBEDDING_SERVER_SOCKET", None)
try:
run_kwargs: dict[str, Any] = {}
if workers != 1:
run_kwargs["workers"] = workers
if limit_concurrency != 1000:
run_kwargs["limit_concurrency"] = limit_concurrency
# Suppress run_server's legacy banner — the click CLI already printed
# a richer one above. Direct `python -m headroom.proxy.server` keeps
# the legacy banner via run_server's default.
run_kwargs["print_banner"] = False
run_server(config, **run_kwargs)
except KeyboardInterrupt:
click.echo("\nShutting down...")
raise SystemExit(130) from None
finally:
if _embed_watchdog is not None:
import asyncio as _asyncio2
_asyncio2.run(_embed_watchdog.stop())
View on GitHub (pinned to 322425c43b)
Solutions
- Treat exit code 130 as clean shutdown in scripts: [ "$rc" -eq 130 ] && echo stopped
- Use a process manager (systemd/docker) that sends SIGINT or configure it to expect 130
- For graceful drains, prefer a SIGTERM-aware entrypoint or run behind uvicorn's own signal handling if you need in-flight request completion
- Watchdog teardown happens in finally — if you see extra errors there, fix the watchdog, not the interrupt path
Example fix
# before (deploy script treats any nonzero as crash) headroom proxy start || exit 1 # after headroom proxy start; rc=$? [ $rc -eq 130 ] && rc=0 # Ctrl-C shutdown is success exit $rc
Defensive patterns
Strategy: try-catch
Try / catch
proc = subprocess.Popen(["headroom", "proxy", "start", ...])
try:
proc.wait()
except KeyboardInterrupt:
proc.send_signal(signal.SIGINT)
rc = proc.wait()
assert rc in (0, 130) # 130 = graceful Ctrl-C shutdown Prevention
- Treat exit 130 as success in wrapper scripts and CI gates
- Send SIGINT (not SIGKILL) to stop the proxy so the finally-block watchdog teardown runs
- For docker, note `docker stop` sends SIGTERM — run under an init wrapper that forwards it as SIGINT or accept the non-graceful path
When it happens
Trigger: Pressing Ctrl-C in the terminal running `headroom proxy ...`, or a supervisor/container sending SIGINT to stop the process.
Common situations: Operators restarting the proxy after config changes; CI wrapping the proxy and interpreting 130 as failure; systemd/docker stop sending SIGINT (note: docker stop sends SIGTERM, which is NOT caught here).
Related errors
- Evaluation interrupted.
- Error: Proxy dependencies not installed. Run: pip install he
- Proxy exited with code {proc.returncode}: {tail}
- Proxy process exited unexpectedly.
- `{' '.join(cmd)}` failed (exit {result.returncode}): {stderr
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/e61b3001af69c204.
Report an issue: GitHub.