Skip to content

fix(codex): preserve streamed text when completed response has empty output#37

Open
sunlei wants to merge 1 commit intobubbuild:mainfrom
sunlei:fix/codex-empty-response-on-completed
Open

fix(codex): preserve streamed text when completed response has empty output#37
sunlei wants to merge 1 commit intobubbuild:mainfrom
sunlei:fix/codex-empty-response-on-completed

Conversation

@sunlei
Copy link
Copy Markdown

@sunlei sunlei commented Apr 7, 2026

Summary

Fix empty response failures on the Codex OAuth Responses path.

The Codex backend sometimes returns a response.completed event carrying an SDK Response object with status="completed" but output=[], even though earlier stream events (output_text.delta) already delivered the full assistant text. _build_response unconditionally returned this empty Response, discarding the collected text and triggering a misleading empty response retry loop.

Related:

Root cause

In OpenAICodexProvider._build_response:

if isinstance(completed_response, Response):
    return completed_response  # unconditional — discards streamed text

The early return did not check whether output was actually populated. When output=[], the already-collected text from stream events was never used to reconstruct the response.

Changes

Narrow the early-return guard in _build_response so reconstruction from streamed data only happens under precise conditions:

  1. output is non-empty → return as-is (output is good)
  2. status != "completed" → return as-is (preserve error/incomplete metadata)
  3. stream collected nothing → return as-is (avoid pointless reconstruction)

Only when all three checks fail (completed + empty output + stream has content) does the method fall through to the existing reconstruction logic via _build_response_output.

Test plan

  • Added regression test: response.completed with a real SDK Response(output=[]) while stream collected text — verifies text is preserved
  • All 127 existing tests pass unchanged

…output

The Codex backend sometimes returns a `response.completed` event
carrying an SDK Response object with `status="completed"` but
`output=[]`, even though earlier stream events already delivered
the full assistant text via `output_text.delta`.

`_build_response` unconditionally returned this empty Response,
discarding the text collected from the stream and causing a
misleading `empty response` retry loop.

- Narrow the early-return guard in `_build_response`: only return
  the SDK Response directly when its output is non-empty
- Return as-is for non-completed status (preserve error metadata)
- Return as-is when the stream collected nothing (avoid pointless
  reconstruction)
- Add regression test with a real SDK Response(output=[])
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