Skip to content

fix: terminate in-progress SSE event before injecting stream_read_error#1479

Open
octo-patch wants to merge 1 commit intoWei-Shaw:mainfrom
octo-patch:fix/issue-1471-sse-blank-line-before-error-event
Open

fix: terminate in-progress SSE event before injecting stream_read_error#1479
octo-patch wants to merge 1 commit intoWei-Shaw:mainfrom
octo-patch:fix/issue-1471-sse-blank-line-before-error-event

Conversation

@octo-patch
Copy link
Copy Markdown

Fixes #1471

Problem

When a read error interrupts an SSE stream mid-event (e.g. stream_read_error), sendErrorEvent() was injecting the synthetic error data: line immediately after flushing the buffer—without first closing the partially-written SSE event with a blank line.

SSE parsers concatenate all data: lines within a single event (i.e., between blank-line separators). So if the upstream had already sent data: {...}\n but the blank-line terminator had not arrived yet, the injected error event ended up appended to the same SSE event:

data: {...}\n          ← incomplete event (no terminating blank line)
data: {"type":"error",...}\n\n  ← synthetic error event

Downstream OpenAI-compatible clients see a single event whose data field contains two concatenated JSON objects, causing parse failures:

  • Could not parse message into JSON
  • Expected ':' after property name in JSON at position 11232 (line 2 column 1)

Solution

Write a bare \n (blank line) after the initial flushBuffered() call inside sendErrorEvent(), so any in-progress SSE event is properly terminated before the error event is written. If no event is in-progress, the extra blank line is harmless—SSE parsers treat consecutive blank lines as empty event separators.

The one-line change is in backend/internal/service/openai_gateway_service.go, inside the handleStreamingResponse function's sendErrorEvent closure.

Testing

  • The fix is a boundary condition in the SSE framing logic. The failure disappears when the current SSE event is explicitly closed before the synthetic error event is written (confirmed by the issue author's local regression test).
  • No behavioral change in the normal (non-error) streaming path.

When a read error interrupts an SSE stream mid-event, sendErrorEvent()
was injecting the synthetic error data line immediately after flushing
the buffer without first closing the partially-written event with a
blank line. SSE parsers concatenate all data lines within a single
event, so the result was two JSON objects in one payload that downstream
clients could not parse. Fix: write a bare newline after the initial
flushBuffered() call so the in-progress event is properly terminated
before the error event begins.

Fixes Wei-Shaw#1471
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenAI /v1/responses streaming can emit malformed SSE after stream_read_error

1 participant