Skip to content

fix: handle null output in response.completed streaming events#3331

Open
xodn348 wants to merge 1 commit into
openai:mainfrom
xodn348:fix/issue-3321
Open

fix: handle null output in response.completed streaming events#3331
xodn348 wants to merge 1 commit into
openai:mainfrom
xodn348:fix/issue-3321

Conversation

@xodn348
Copy link
Copy Markdown

@xodn348 xodn348 commented May 29, 2026

Summary

Fixes #3321

When the server sends output: null in the response.completed SSE event, the SDK crashes with a TypeError in parse_response because it iterates over response.output without a null check. Existing PRs (e.g. #3327) only add the defensive or [] guard, which prevents the crash but silently discards all output items that were correctly accumulated from response.output_item.added events during streaming.

This PR adds two targeted fixes:

  1. Defensive null guard in parse_responseresponse.output or [] prevents the TypeError
  2. Snapshot fallback in ResponseStreamState.accumulate_event — when response.completed has output: null, falls back to snapshot.output (the items accumulated from prior response.output_item.added events) so no data is lost

Changes

  • src/openai/lib/_parsing/_responses.py — null-safe iteration over response.output
  • src/openai/lib/streaming/responses/_responses.py — snapshot fallback in response.completed handler
  • tests/lib/responses/test_null_output.py — 3 tests covering null output parsing and snapshot fallback

Test plan

  • parse_response with output=None returns empty output (no crash)
  • parse_response with output=[] returns empty output (baseline)
  • Streaming accumulator preserves snapshot output when response.completed has output=None
  • All existing tests/lib/responses/ tests pass (8/8)
  • pyright: 0 errors
  • ruff: all checks passed

When the server sends `output: null` in the `response.completed` SSE
event, the SDK would crash with a TypeError in `parse_response`. This
adds two defenses:

1. Defensive null guard in `parse_response` (`response.output or []`)
2. Snapshot fallback in the streaming accumulator — if the completed
   event has null output but the snapshot accumulated items from
   `response.output_item.added` events, use those instead of losing them

Fixes openai#3321

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@xodn348 xodn348 requested a review from a team as a code owner May 29, 2026 05:15
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9cbc5cf0cd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

elif event.type == "response.completed":
completed_response = event.response
if completed_response.output is None and snapshot.output is not None: # pyright: ignore[reportUnnecessaryComparison]
completed_response = completed_response.model_copy(update={"output": snapshot.output})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Pydantic v1 compatibility when copying the response

In environments using Pydantic v1, which this package still supports via pydantic>=1.9.0, <3, Response instances do not have a model_copy method. When a streamed response.completed event has output: null and prior output items were accumulated, this fallback path will raise AttributeError before parsing, so the crash remains for supported Pydantic v1 users. Use the repository's compatibility helper or the v1 copy(update=...) path here.

Useful? React with 👍 / 👎.

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.

responses.stream() crashes with NoneType iteration when response.completed payload has output=null

1 participant