Prevent streaming errors from terminating llm-bridge #27

targeting main from deleted fork:fix/repeated-502-crash
Closed opened by and assigned to callie.on-her.computer
targeting main from deleted fork:fix/repeated-502-crash

The bridge dies when an upstream SSE body fails after response headers are committed. The /v1/responses catch attempts to send a JSON 500, Fastify attempts a second header write, and ERR_HTTP_HEADERS_SENT escapes as an unhandled rejection. Node exits with status 1. The homura journal showed restart counter 14.

The fix claims each direct raw streaming reply with reply.hijack() before writing headers. Protocol error paths and a server-wide Fastify error handler detect a committed response, close only that response, and do not send another header block. The upstream stream error still propagates to the route catch, so partial output is not recorded as a completed assistant turn.

The exact regression creates a Codex SSE response that emits one valid partial event and then faults its body. Untouched main reports ERR_HTTP_HEADERS_SENT from Fastify's fallback error handler as an unhandled rejection. The fixed server terminates that client stream and continues serving /health. A second regression covers an otherwise uncaught route error after raw headers are committed.

Scope is limited to src/server.ts, src/antigravity.ts, and src/server.test.ts. The earlier restart/startup changes are not in the final review round.

Verification:

  • Exact committed-stream crash regressions pass
  • Server suite passes 27/27
  • TypeScript typecheck passes
  • Prettier check passes
  • Production admin build passes
  • Nix package builds as /nix/store/8as9xif3601n04klvwxacn48l55wz0rw-llm-bridge-0.1.0
  • Full suite reports 251 passed, 0 failed, and 2 cancelled
  • The same two upstream-errors.test.ts cancellations reproduce on untouched origin/main at 53f96a0
  • git diff --check passes
Commits 0