Skip to content

Streaming: JSONDecodeError when SSE events contain only meta-fields (retry, id, event) #2722

@c4milo

Description

@c4milo

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 processing

Pull Request

Fix submitted in #2721

The PR includes:

  • Checks in both Stream.__stream__() and AsyncStream.__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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions