Skip to content

fix(codex): handle Responses stream whose completed event has output=null - #33050

Closed
sree-sanak wants to merge 1 commit into
NousResearch:mainfrom
sree-sanak:codex-none-output-fix
Closed

fix(codex): handle Responses stream whose completed event has output=null#33050
sree-sanak wants to merge 1 commit into
NousResearch:mainfrom
sree-sanak:codex-none-output-fix

Conversation

@sree-sanak

Copy link
Copy Markdown

Problem

The ChatGPT Codex backend (https://chatgpt.com/backend-api/codex, gpt-5.x) streams the answer via response.output_item.done / response.output_text.delta events, then sends the terminal response.completed event with response.output = null.

openai-python's Responses streaming accumulator calls parse_response() on that completed event, and parse_response does:

for output in response.output:

with no None guard (observed on openai==2.24.0, openai/lib/_parsing/_responses.py). When response.output is null, this raises TypeError: 'NoneType' object is not iterable mid-stream.

run_codex_stream only catches httpx/RuntimeError, so the TypeError propagates and every request against that backend fails. Schedulers that wrap it re-surface it as RuntimeError: 'NoneType' object is not iterable.

Fix

  • run_codex_stream: catch the parse_response None-output TypeError and route to the existing create(stream=True) fallback, which iterates SSE events manually and never calls parse_response. This mirrors how the code already handles the response.created/response.completed prelude/postlude quirks for this same backend.
  • run_codex_create_stream_fallback and the auxiliary Codex adapter: backfill output from the collected stream items when the terminal output is None (previously only handled the empty-list case), so the already-streamed content is recovered instead of lost.

No behavior change for backends that send a well-formed response.completed.

Verification

Reproduced against the live backend (the streamed events arrive, the terminal response.completed carries output: null, and the SDK raises the TypeError). With the fix, requests that previously crashed return their content normally. Both the main conversation path and the auxiliary path (title generation, etc.) recover.

…null

The chatgpt.com/backend-api/codex backend (gpt-5.x) streams the answer via
response.output_item.done / response.output_text.delta events, then sends
the terminal response.completed event with response.output = null.

openai-python's Responses streaming accumulator calls parse_response() on
that completed event, which does `for output in response.output` with no
None guard (seen on openai 2.24.0, lib/_parsing/_responses.py), raising
TypeError: 'NoneType' object is not iterable mid-stream. run_codex_stream
only catches httpx/RuntimeError, so it propagates and fails every request
against that backend.

- run_codex_stream: catch the parse_response None-output TypeError and route
  to the existing create(stream=True) fallback, which iterates events
  manually and never calls parse_response.
- run_codex_create_stream_fallback and the auxiliary Codex adapter: backfill
  output from collected stream items when the terminal output is None, not
  only when it is an empty list, so already-streamed content is recovered.

Verified against the live backend: requests that previously crashed now
return their content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have codex comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels May 27, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Duplicate of #32963 (merged) — same Codex response.output=null stream recovery in codex_runtime.py and auxiliary_client.py. Root cause: #11179.

@teknium1

Copy link
Copy Markdown
Contributor

Closing as obsolete. PR #33042 (merged commit cb38ce28c) removes the OpenAI SDK's client.responses.stream(...) helper from both Codex call sites entirely — we now use client.responses.create(stream=True) raw event iteration that assembles the final response from response.output_item.done events as they arrive. The terminal event's output field is never read for content reconstruction, so the SDK's TypeError: 'NoneType' object is not iterable parser crash on output=null is structurally impossible — no code path can produce it. Same defensive strategy OpenClaw uses for the same backend.

The symptom-patch + recovery logic this PR added on top of the SDK helper is now redundant. Thanks for the work during the outage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants