#3604·headroom

Persistent-service crash loop on macOS 26 (Darwin 27): PyTorch MPS backend aborts on IOGPUMetalCommandBuffer during embedder/kompress model load

Author: MirabisCreated Sep 15, 2026Updated Sep 15, 2026

Description

The persistent-service deployment enters a hard crash loop on macOS 26 (Darwin 27). It restarts roughly every 60 seconds continuously (200+ crashes observed in one session). Each crash is a native Metal assertion abort during PyTorch MPS model load, not a Python exception, so nothing in headroom can catch or retry around it.

Root cause: the generated deployment config sets both HEADROOM_EMBEDDER_RUNTIME=pytorch_mps (memory embedder, headroom/memory/adapters/embedders.py) and HEADROOM_KOMPRESS_BACKEND=pytorch_mps (compression model, headroom/transforms/kompress_compressor.py), forcing PyTorch onto the Apple Silicon MPS/Metal backend. HEADROOM_KOMPRESS_BACKEND=pytorch_mps has a try/except around load-time validation that falls back to ONNX/CPU on failure, but this crash is a native Metal abort that kills the whole process before Python gets a chance to catch it, so the fallback path never runs. HEADROOM_EMBEDDER_RUNTIME=pytorch_mps has no fallback at all.

This looks tied to a macOS 26 upgrade (crashes started the day after upgrading) — PyTorch's MPS backend commonly lags behind new macOS majors before Apple/PyTorch re-validate compatibility.

To Reproduce

Steps to reproduce the behavior:

  1. Install headroom with the persistent-service preset on Apple Silicon running macOS 26 (Darwin 27), with HEADROOM_EMBEDDER_RUNTIME=pytorch_mps and/or HEADROOM_KOMPRESS_BACKEND=pytorch_mps set (this is the default in the generated run-headroom.sh/ensure-headroom.sh for a persistent-service deployment)
  2. Start/restart the deployment: headroom install restart
  3. Watch ~/.headroom/deploy/default/runner.log — the proxy starts, then aborts within seconds during embedder/kompress model load; launchd respawns it and it crashes again on a ~60s cycle

Expected Behavior

The proxy should either load the model successfully, or fail gracefully by falling back to a CPU/ONNX backend, without entering an unrecoverable crash loop that leaves the deployment permanently unhealthy.

Actual Behavior

The process aborts with a native Metal assertion immediately after Starting proxy server..., before the proxy becomes healthy. headroom install status alternates between running/healthy: yes and healthy: no as launchd keeps respawning the process. Every client routed through the proxy (e.g. Claude Code via ANTHROPIC_BASE_URL) intermittently sees API Error: Connection lost mid-response when a request lands mid-crash.

Code Sample

# Not a client-code issue — the crash happens inside the headroom proxy's
# own model-loading path (memory embedder / kompress compressor), triggered
# simply by running the persistent-service deployment with the pytorch_mps
# backend selected. No user code is involved.

Error Output

2026-09-15 21:08:18.176 python3[52375:4668384] failed assertion _status < MTLCommandBufferStatusCommitted at line 323 in -[IOGPUMetalCommandBuffer setCurrentCommandEncoder:]
Starting proxy server...
2026-09-15 21:09:23.790 python3[55198:4677048] failed assertion _status < MTLCommandBufferStatusCommitted at line 323 in -[IOGPUMetalCommandBuffer setCurrentCommandEncoder:]
Starting proxy server...
2026-09-15 21:10:27.087 python3[60362:4687421] failed assertion _status < MTLCommandBufferStatusCommitted at line 323 in -[IOGPUMetalCommandBuffer setCurrentCommandEncoder:]
Starting proxy server...

(repeats on a ~60s cycle, 200+ occurrences in the log)

Environment

  • Headroom version: 0.37.0
  • Python version: 3.13.13
  • OS: macOS 26.0 (Darwin 27.0.0, build 26A428), Apple Silicon (arm64)
  • LLM Provider: Anthropic

Additional Context

  • torch version: 2.14.0
  • Deployment: persistent-service preset, launchd-managed (com.headroom.default)
  • Suggested fix: detect repeated rapid restarts and auto-downgrade HEADROOM_EMBEDDER_RUNTIME/HEADROOM_KOMPRESS_BACKEND to cpu/onnx for that deployment, or at least surface an actionable error via headroom doctor instead of a silent restart cycle. Also worth reconsidering defaulting persistent-service deployments to pytorch_mps on macOS given how brittle Metal/MPS compatibility is across OS versions.
  • Workaround: manually setting HEADROOM_EMBEDDER_RUNTIME=cpu and HEADROOM_KOMPRESS_BACKEND=onnx (or unset, since auto prefers ONNX CPU) in the deployment's run-headroom.sh/ensure-headroom.sh, then headroom install restart, resolves it.

Source: headroomlabs-ai/headroom