Skip to content

Fix Codex Responses streaming crash on null-output snapshots#453

Open
Wondermonger-daydreaming wants to merge 1 commit into
math-inc:mainfrom
Wondermonger-daydreaming:fix/codex-responses-null-output-streaming
Open

Fix Codex Responses streaming crash on null-output snapshots#453
Wondermonger-daydreaming wants to merge 1 commit into
math-inc:mainfrom
Wondermonger-daydreaming:fix/codex-responses-null-output-streaming

Conversation

@Wondermonger-daydreaming
Copy link
Copy Markdown

Problem

Every gauss chat turn using the openai-codex provider (i.e. a ChatGPT subscription, chatgpt.com/backend-api/codex) aborts immediately with:

TypeError: 'NoneType' object is not iterable

Root cause

The ChatGPT Codex Responses backend deviates from the public api.openai.com Responses API:

  • It emits streaming SSE snapshot events with output=None (the public API uses output=[]).
  • The OpenAI SDK's responses.stream() accumulator calls parse_response() on every event, which does for output in response.output: — this raises TypeError: 'NoneType' object is not iterable on the first event (response.created) of every Codex turn.
  • _run_codex_stream only falls back on a RuntimeError (missing response.completed), so this TypeError propagates and the request is treated as a non-retryable client error.
  • The backend also leaves .output None on the terminal response.completed event — the completed items arrive only via response.output_item.done, and text only via response.output_text.delta. So even the manual fallback returned a response whose .output / .output_text access crashed downstream.

Note: this is a backend deviation, not documented OpenAI behavior — bumping the openai SDK does not fix it, and guarding the SDK's parse_response yields empty text (the accumulator has no populated output to merge deltas onto).

Fix

Two surgical changes in run_agent.py:

  1. _run_codex_stream — catch the accumulator TypeError and route to the existing manual create(stream=True) fallback.
  2. _run_codex_create_stream_fallback — collect response.output_item.done items during iteration and reconstruct the terminal response's .output via a new _reconstruct_codex_output() helper (no-op when .output is already populated, so non-Codex Responses backends are unaffected).

Verification

Tested against a live ChatGPT (Codex) subscription:

  • ✅ Default model
  • ✅ Pinned gpt-5.3-codex
  • ✅ Tool-using turns (read_file → correct result)

Each path previously failed with the TypeError; all now return correctly.

The ChatGPT Codex backend (chatgpt.com/backend-api/codex) emits Responses
SSE snapshot events with `output=None`. The OpenAI SDK's responses.stream()
accumulator calls parse_response() on every event, which iterates
`response.output` and raises "'NoneType' object is not iterable" on the very
first event of every Codex turn — making the openai-codex provider (i.e. a
ChatGPT subscription) unusable.

- _run_codex_stream: catch the accumulator TypeError and route to the manual
  create(stream=True) fallback. Previously only a RuntimeError for the
  missing-`response.completed` case fell back, so this TypeError aborted.
- _run_codex_create_stream_fallback: collect `response.output_item.done`
  items and reconstruct the terminal response's `.output` via the new
  _reconstruct_codex_output helper. The Codex backend leaves `.output` None
  on the terminal event; text/items arrive only via the streamed delta and
  output_item.done events, so `.output` / `.output_text` must be rebuilt.

Verified against a live ChatGPT (Codex) subscription: default model, pinned
gpt-5.3-codex, and tool-using turns all return correctly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant