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", line82, in__stream__data=sse.json() File"openai/_streaming.py", line259, injsonreturnjson.loads(self.data) json.decoder.JSONDecodeError: Expectingvalue: line1column1 (char0)

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:

fromopenaiimportOpenAIclient=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 directivesstream=client.chat.completions.create( model="gpt-4", messages=[{"role": "user", "content": "Hello"}], stream=True ) forchunkinstream: 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__()forsseiniterator: ifsse.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 JSONifnotsse.dataorsse.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