#4300: Gateway: prevent OpenAI-compatible client crash on SSE termination
gateway
Cluster:
Gateway Error Handling Improvements
### What
Some OpenAI-compatible clients crash or throw when the SSE stream ends without a terminal `chat.completion.chunk` containing `finish_reason`.
### Fix
- Emit a final `chat.completion.chunk` with `finish_reason: "stop"` (and empty `delta`) before sending `data: [DONE]`.
- Keep content-part parsing compatible with both `{ type: "input_text", input_text: "..." }` and older `{ type: "input_text", text: "..." }` shapes.
### Tests
- `pnpm vitest run --config vitest.e2e.config.ts src/gateway/openai-http.e2e.test.ts`
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR adjusts the OpenAI-compatible `/v1/chat/completions` SSE streaming implementation to avoid client crashes on stream termination.
- Adds a helper to emit a terminal `chat.completion.chunk` (with `finish_reason`) immediately before `data: [DONE]`.
- Refactors stream shutdown into a shared `endStream()` path used for lifecycle end/error and the `finally` cleanup.
- Updates content-part parsing to accept both `{type: "input_text", input_text: "..."}` and the older `{type: "input_text", text: "..."}` shapes.
Overall this fits the existing gateway approach: it continues to proxy agent events into OpenAI-style chunks while keeping request parsing permissive for multiple client payload formats.
<h3>Confidence Score: 4/5</h3>
- This PR is generally safe to merge and addresses a real interoperability issue, with a couple of small cleanup/semantics nits.
- The change is localized to SSE termination behavior and request content parsing, with minimal surface area. The new `endStream()` reduces duplicated shutdown logic and should prevent premature `[DONE]` without a terminal chunk. Remaining concerns are: listener cleanup (`req.on('close')`) and distinguishing normal vs error termination in the terminal chunk semantics.
- src/gateway/openai-http.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#16994: fix(gateway): prevent double terminal SSE event on OpenResponses error
by AI-Reviewer-QS · 2026-02-15
79.7%
#9218: Fix Control UI chat resync on gaps and terminal events
by figitaki · 2026-02-05
78.4%
#14309: fix(ui): resolve chat event session key mismatch
by justonlyforyou · 2026-02-11
78.0%
#13235: feat: stream reasoning_content via /v1/chat/completions SSE
by mode80 · 2026-02-10
77.2%
#4534: fix: packaging and OpenAI vision format conversion
by SalimBinYousuf1 · 2026-01-30
76.9%
#11101: fix: handle AbortError and WebSocket 1006 in unhandled rejection ha...
by Nipurn123 · 2026-02-07
76.5%
#10034: Don't crash gateway on transient unhandled fetch failures
by gigq · 2026-02-06
76.4%
#12314: fix: treat HTTP 5xx server errors as failover-worthy
by hsssgdtc · 2026-02-09
75.9%
#10273: fix(agents): detect and auto-compact mid-run context overflow
by terryops · 2026-02-06
75.7%
#12687: fix: handle empty LLM stream response with failover
by janckerchen · 2026-02-09
75.5%