fix(codex): translate SDK TypeError on output=None to classified error (#33976) - #34265
Closed
Bartok9 wants to merge 1 commit into
Closed
fix(codex): translate SDK TypeError on output=None to classified error (#33976)#34265Bartok9 wants to merge 1 commit into
Bartok9 wants to merge 1 commit into
Conversation
NousResearch#33976) Fixes NousResearch#33976. The ChatGPT Pro Codex OAuth subscription path (chatgpt.com/backend-api/codex/responses) has been observed intermittently returning HTTP 200 with response.output=None on the terminal response.completed event. The OpenAI SDK's internal parse_response then raises: TypeError: 'NoneType' object is not iterable …from 'for output in response.output:' in _parsing/_responses.py:61, BEFORE Hermes's adapter can intervene. This propagates up as a raw stack trace to the user-facing layer, breaking Slack gateway requests and any cron job using the Codex provider until the user manually switches to OpenRouter/Claude/etc. Hermes already consumes raw events directly to avoid relying on the SDK's typed-response reconstruction (the streaming kwargs return an event iterator), but the SDK still does its own internal parse on stream close — so the TypeError still surfaces. Fix: defensive TypeError catch around the stream consumer at both call sites (auxiliary_client for Slack/cron, codex_runtime for interactive). When the exception matches the specific 'NoneType is not iterable' signature, translate to a classified RuntimeError with an actionable message naming the endpoint and the workaround. The catch is intentionally narrow — only the exact failure-mode message gets translated. Any other TypeError propagates uncaught because it indicates a real bug, not a backend regression. Adds 4 regression tests in tests/agent/test_codex_malformed_response_33976.py covering both call sites and the narrow-catch invariant. Co-authored-by: Cursor <cursoragent@cursor.com>
Contributor
|
Thanks for this — closing as already fixed on The Codex null-output crash (
Your fix targets the same path that's now hardened upstream, so there's nothing left to merge here. Your authorship is preserved in your branch; I'm crediting your report in the umbrella issue #33932 (now closed). Appreciate the contribution. |
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #33976.
Problem
The ChatGPT Pro Codex OAuth subscription path (
chatgpt.com/backend-api/codex/responses) has been observed intermittently returning HTTP 200 withresponse.output = Noneon the terminalresponse.completedevent. The OpenAI SDK's internalparse_responsethen raises:This propagates as a raw stack trace through Hermes to the user-facing layer:
TypeErrormessagesWhy Hermes' existing raw-event consumer doesn't catch it
Hermes already uses
responses.create(stream=True)and consumes raw events directly to avoid relying on the SDK's typed-response reconstruction. However, the SDK still does its own internalparse_responseon stream close — so theTypeErrorraised fromfor output in response.output:still bubbles up to our_consume_codex_event_streamcaller.Fix
Defensive
TypeErrorcatch around the stream consumer at both call sites:agent/auxiliary_client.py(Slack / cron gateway path)agent/codex_runtime.py(main agent loop path)When the exception matches the specific
'NoneType' object is not iterablesignature, translate to a classifiedRuntimeErrorwith an actionable message:The catch is intentionally narrow — only the exact failure-mode message gets translated. Any other
TypeErrorpropagates because it indicates a real bug, not a backend regression. The original cause is preserved viaraise ... from excso debug logging can still see the SDK trace.Tests
4 new tests in
tests/agent/test_codex_malformed_response_33976.py:test_typeerror_with_none_iterable_is_translated(aux)test_other_typeerror_messages_still_propagate(aux)test_runtime_translates_none_iterable_typeerror(runtime)test_unrelated_typeerror_still_propagates_in_runtimeCredit to @szuhodov for the precise root-cause analysis including the exact SDK file/line and the workaround config.
Co-authored-by: Cursor cursoragent@cursor.com