Skip to content

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

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-5bf34d29
May 27, 2026
Merged

fix(auxiliary): coerce None final.output to empty list in Codex aux adapter#33386
teknium1 merged 1 commit into
mainfrom
hermes/hermes-5bf34d29

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Closes #33368.

Summary

_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. 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

  • New regression test test_handles_final_output_is_none_after_consumer monkey-patches the consumer to return a final with .output=None and asserts the adapter handles it without raising
  • Pre-existing test test_recovers_output_item_when_terminal_event_has_null_output continues passing
  • tests/agent/test_auxiliary_client.py::TestCodexAuxiliaryAdapterNullOutputRecovery → 2/2 passing

Attribution

Reporter: @pavegrid-1 (#33368).

…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).
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-5bf34d29 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9507 on HEAD, 9506 on base (🆕 +1)

🆕 New issues (1):

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.

@teknium1 teknium1 merged commit 486d632 into main May 27, 2026
24 checks passed
@teknium1 teknium1 deleted the hermes/hermes-5bf34d29 branch May 27, 2026 18:08
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent loop, run_agent.py, prompt builder codex P3 Low — cosmetic, nice to have duplicate This issue or pull request already exists labels May 27, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Duplicate of merged #32963 which already landed the same null-output coerce fix for auxiliary_client.py. The root issue #33368 was also flagged as a duplicate of #32974/#11179. This PR is a late-arriving fix for an issue already resolved on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex comp/agent Core agent loop, run_agent.py, prompt builder duplicate This issue or pull request already exists 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.

Codex auxiliary title generation crashes when final output is None

2 participants