Skip to content

fix(streaming): skip empty SSE data events and move error-event check out of thread. block#2943

Open
s-zx wants to merge 1 commit intoopenai:mainfrom
s-zx:fix/streaming-dead-code-empty-sse
Open

fix(streaming): skip empty SSE data events and move error-event check out of thread. block#2943
s-zx wants to merge 1 commit intoopenai:mainfrom
s-zx:fix/streaming-dead-code-empty-sse

Conversation

@s-zx
Copy link

@s-zx s-zx commented Mar 8, 2026

Summary

Two related bugs in Stream.__stream__ and AsyncStream.__stream__ (src/openai/_streaming.py), both sync and async paths.

Bug 1: JSONDecodeError on meta-only SSE events (closes #2722)

The SSE specification allows events that carry only meta-fields (retry:, id:, event:) with no data field. The SDK's SSE parser correctly handles these by setting data = "", but __stream__ called sse.json() unconditionally:

data = sse.json()   # JSONDecodeError when sse.data == ""

This crashes with JSONDecodeError: Expecting value: line 1 column 1 (char 0) when any API gateway or proxy injects an SSE retry: directive.

Fix: skip events whose data field is empty or whitespace before any JSON parsing.

Bug 2: Unreachable sse.event == "error" check (closes #2796)

The error-event guard was nested inside the if sse.event.startswith("thread.") branch:

if sse.event and sse.event.startswith("thread."):
    data = sse.json()
    if sse.event == "error" and ...:   # ← DEAD CODE: "error" never starts with "thread."
        raise APIError(...)

Since "error" does not start with "thread.", this check can never be true — it's dead code and was a regression from commit abc25966. The error-event handling belongs in the else branch (non-thread events).

Fix: move the sse.event == "error" guard to the else branch.

Changes

  • src/openai/_streaming.py — sync Stream.__stream__ and async AsyncStream.__stream__
    • Add guard: if not sse.data or not sse.data.strip(): continue
    • Relocate sse.event == "error" check from thread. block to else block

…ad. block

Two related bugs in Stream.__stream__ and AsyncStream.__stream__:

1. JSONDecodeError on meta-only SSE events (fixes openai#2722)
   The SSE specification allows events with no data field (e.g. standalone
   'retry:' or 'id:' directives).  The SDK's SSE parser correctly sets
   data='' for these but __stream__ called sse.json() unconditionally,
   raising JSONDecodeError: Expecting value.
   Fix: skip events whose data is empty or whitespace before any JSON
   parsing.

2. Unreachable sse.event == 'error' check (fixes openai#2796)
   The error-event guard was nested inside the startswith('thread.') branch,
   making it logically impossible to trigger because 'error' != 'thread.*'.
   This was a regression from commit abc2596 ('fix(streaming): correct
   indentation') where the check was accidentally moved inside the block.
   Fix: move the error-event handling to the else branch (non-thread events),
   which is the correct location for standalone 'error' SSE events.
@s-zx s-zx requested a review from a team as a code owner March 8, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant