Skip to content

fix(gateway): guard Codex null output and provider SDK errors - #33103

Closed
trymhaak wants to merge 2 commits into
NousResearch:mainfrom
trymhaak:fix/codex-provider-guard-origin-main
Closed

fix(gateway): guard Codex null output and provider SDK errors#33103
trymhaak wants to merge 2 commits into
NousResearch:mainfrom
trymhaak:fix/codex-provider-guard-origin-main

Conversation

@trymhaak

Copy link
Copy Markdown
Contributor

Summary

  • Guard Codex Responses streaming against SDK Response.output=None snapshots by normalizing the value before parse_response consumes it.
  • Wrap bare provider SDK TypeError/AttributeError failures into a user-visible provider error envelope instead of letting raw Python exceptions surface in Telegram.
  • Add regression coverage for the Codex null-output guard and Telegram/provider error sanitization path.

Test plan

  • python -m pytest tests/run_agent/test_codex_stream_null_output_guard.py tests/run_agent/test_provider_sdk_error_envelope.py -q -o 'addopts='

Operational notes

  • This branch was rebuilt in a clean worktree from current origin/main and cherry-picked from the earlier local incident branch.
  • No gateway restart/deploy has been performed from this branch.

Test and others added 2 commits May 27, 2026 09:48
chatgpt.com's backend-api/codex emits early streaming snapshots where
``response.output`` is ``null``. openai 2.24.0 iterates that field
without a None-guard at lib/_parsing/_responses.py:61 (called from
lib/streaming/responses/_responses.py:360 via accumulate_event), which
raises ``TypeError: 'NoneType' object is not iterable`` mid ``for event
in stream`` — aborting every codex turn before our existing guards in
run_codex_stream / _normalize_codex_response can normalize the
snapshot.

We can't move the openai pin (pyproject documents that other releases
segfault pydantic-core), so install a surgical, idempotent shim at
codex_runtime import time. The shim wraps ``parse_response`` in BOTH
namespaces it's bound in (origin + the streaming module's re-import)
and coerces ``response.output`` to ``[]`` before delegating, leaving
all other behaviour untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When openai 2.24.0's accumulate_event iterates a streaming snapshot
whose response.output is null, it raises a bare
``TypeError("'NoneType' object is not iterable")`` mid-``for event in
stream``. Commit a0c90a190 installs an SDK-level shim to prevent the
crash, but the agent's outer retry loop still needs to be hardened
against future provider/SDK leaks of the same shape: after max retries
the user-facing reply was ``"API call failed after 5 retries: 'NoneType'
object is not iterable"`` — gibberish in a Telegram bubble and worse on
CLI/Slack/web paths that skip the Telegram-only sanitizer.

Wrap bare TypeError/AttributeError whose ``str(error)`` matches Python's
stock ``"'<TypeName>' object …"`` shape (a signature that real provider
HTTP bodies do not emit) in a ``Provider SDK streaming bug`` envelope
inside ``AIAgent._summarize_api_error``. Only fires when
``status_code`` is unset, so errors that carry real provider HTTP
context still flow through the existing body/HTTP fallback paths
unchanged.

Extend ``_gateway_provider_error_reply`` with a targeted Telegram reply
for the new envelope so users get "malformed streaming response" rather
than the generic provider-failed-after-retries fallback. The composed
final reply ``"API call failed after N retries: Provider SDK streaming
bug …"`` still triggers ``_looks_like_gateway_provider_error``'s shape
regex, so the Telegram sanitizer remains the second line of defence.

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 comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery codex labels May 27, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Closing as obsolete. PR #33042 (merged commit cb38ce2) removes the OpenAI SDK's responses.stream(...) helper from both Codex call sites entirely. The responses.create(stream=True) raw event iteration we now use never reads response.completed.response.output for content reconstruction, so the SDK's null-output TypeError is structurally impossible — there's no parse_response snapshot to normalize before. Same defensive strategy OpenClaw uses for the same backend. The provider-SDK error envelope sanitization is a separate concern; if you'd like to land just that piece as a focused PR, happy to review. Thanks for the work.

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 comp/gateway Gateway runner, session dispatch, delivery 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