fix(auxiliary): coerce None final.output to empty list in Codex aux adapter

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).
This commit is contained in:
teknium1 2026-05-27 11:01:47 -07:00 committed by Teknium
parent 9919caff46
commit 486d632cc2
2 changed files with 58 additions and 1 deletions

View file

@ -828,7 +828,7 @@ class _CodexCompletionsAdapter:
val = obj.get(key, default)
return val if val is not None else default
for item in getattr(final, "output", []):
for item in (getattr(final, "output", None) or []):
item_type = _item_get(item, "type")
if item_type == "message":
for part in (_item_get(item, "content") or []):