fix(auxiliary): coerce None final.output to empty list in Codex aux adapter#33386
Merged
Conversation
…dapter Closes #33368. `_CodexCompletionsAdapter.create()` iterates `final.output` from the Codex Responses stream. The event-driven consumer (introduced in #33042) always sets `final.output` to a list, so this shape can't come from our own code path. But: - Mocked clients in tests can return a typed Response with `output=None` - Third-party shims / compatibility layers that bypass the consumer can do the same - A future code path that wraps a different consumer could regress The old code `getattr(final, "output", [])` returns `None` (not the default `[]`) when the attribute EXISTS but is `None`. Iterating `None` then raises `TypeError: 'NoneType' object is not iterable` — the exact error logged by title-generation when this fires. Fix: `getattr(final, "output", None) or []` — single-line defensive coerce. Cheap; zero risk. Regression test asserts the auxiliary path handles a final whose `.output` is `None` (via monkey-patched consumer) without raising and returns the expected chat.completions-shaped response. Reporter: @pavegrid-1 (issue #33368).
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-assignment |
1 |
First entries
tests/agent/test_auxiliary_client.py:2635: [invalid-assignment] invalid-assignment: Object of type `def _consume_returning_none_output(...) -> Unknown` is not assignable to attribute `_consume_codex_event_stream` of type `def _consume_codex_event_stream(event_iter: Any, *, model: str, on_text_delta=None, on_reasoning_delta=None, on_first_delta=None, on_event=None, interrupt_check=None) -> SimpleNamespace`
✅ Fixed issues: none
Unchanged: 5005 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
Collaborator
This was referenced May 27, 2026
fix(agent): classify TypeError('NoneType ... not iterable') as retryable provider shape error
#33399
Merged
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.
Closes #33368.
Summary
_CodexCompletionsAdapter.create()iteratesfinal.outputfrom the Codex Responses stream. The event-driven consumer introduced in #33042 always setsfinal.outputto a list, so this shape can't come from our own code path. But:output=NoneThe old code
getattr(final, "output", [])returnsNone(not the default[]) when the attribute exists but isNone— iteratingNonethen raisesTypeError: 'NoneType' object is not iterable. That's the exact error the title-generation log line was emitting.Fix
Single-line defensive coerce:
getattr(final, "output", None) or []. Cheap, zero risk.Validation
test_handles_final_output_is_none_after_consumermonkey-patches the consumer to return a final with.output=Noneand asserts the adapter handles it without raisingtest_recovers_output_item_when_terminal_event_has_null_outputcontinues passingtests/agent/test_auxiliary_client.py::TestCodexAuxiliaryAdapterNullOutputRecovery→ 2/2 passingAttribution
Reporter: @pavegrid-1 (#33368).