-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Description
Description
The OpenAI Python SDK's streaming implementation throws a JSONDecodeError when encountering SSE (Server-Sent Events) events that contain only meta-fields (such as retry, id, or event) but no data field.
Error
File "openai/_streaming.py", line 82, in __stream__
data = sse.json()
File "openai/_streaming.py", line 259, in json
return json.loads(self.data)
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)Root Cause
Per the SSE specification (WHATWG HTML § 9.2), meta-only events are valid. For example:
retry: 3000
data: {"id":"msg_123",...}
The SDK's SSE decoder correctly parses these events, setting the retry field but leaving data empty (empty string). However, Stream.__stream__() and AsyncStream.__stream__() attempt to call .json() on all events, including those with empty data, leading to the error.
Reproduction
This occurs when streaming from SSE sources that send retry directives or other meta-only events. Example:
from openai import OpenAI
client = OpenAI(
api_key="test",
base_url="http://gateway-that-sends-retry" # Any gateway/proxy that includes SSE retry
)
# This will fail with JSONDecodeError if the stream includes retry directives
stream = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello"}],
stream=True
)
for chunk in stream:
print(chunk)Impact
This affects users who:
- Use API gateways or proxies that add SSE retry directives
- Work with streaming APIs that follow the SSE spec and include meta-fields
- Need reliable streaming in production environments
Proposed Solution
Add a check to skip events with empty/whitespace data before attempting JSON parsing:
# In Stream.__stream__() and AsyncStream.__stream__()
for sse in iterator:
if sse.data.startswith("[DONE]"):
break
# Skip events with no data (e.g., standalone retry/id directives)
# Per SSE spec, these are valid meta-only events that shouldn't be parsed as JSON
if not sse.data or sse.data.strip() == "":
continue
# ... rest of processingPull Request
Fix submitted in #2721
The PR includes:
- Checks in both
Stream.__stream__()andAsyncStream.__stream__() - Test cases for retry directives and meta-only events
- Verification with production streaming workloads
Environment
- OpenAI Python SDK version: 2.6.1 (latest)
- Python version: 3.14
- Operating system: macOS