Skip to content

Tolerate missing Responses output when aggregating text#3315

Open
iqdoctor wants to merge 1 commit into
openai:mainfrom
iqdoctor:fix/responses-none-output-diagnostic
Open

Tolerate missing Responses output when aggregating text#3315
iqdoctor wants to merge 1 commit into
openai:mainfrom
iqdoctor:fix/responses-none-output-diagnostic

Conversation

@iqdoctor
Copy link
Copy Markdown

Summary

Guard Responses parsing and the Response.output_text convenience accessor when a Response instance has output=None.

Today these paths can leak a raw Python error:

TypeError: 'NoneType' object is not iterable

Observed iteration sites:

  • openai/lib/_parsing/_responses.py: for output in response.output
  • openai/types/responses/response.py: for output in self.output

Ontology / boundary

  • Malformed or partial response shape: output is absent/null on a Responses object.
  • SDK invariant: SDK parser/accessor code should not expose raw Python iteration errors for this shape.
  • Application responsibility: applications can still decide whether an empty output_text is useful, but they should not need to reverse-engineer a NoneType iterable failure.

This PR takes the minimal compatibility-preserving approach: treat missing output as an empty list at the iteration boundary.

If maintainers prefer a stricter policy, the alternative would be a typed validation/API-response error that identifies response.output; either is preferable to leaking raw TypeError.

Verification

uv run pytest -q tests/lib/responses/test_responses.py

7 passed

@iqdoctor
Copy link
Copy Markdown
Author

Related downstream canonical Hermes PR that motivated and validates this SDK invariant issue:

NousResearch/hermes-agent#32884

That Hermes PR recovers already-streamed Codex output when the final Responses object has output=None and openai-python currently raises TypeError: 'NoneType' object is not iterable during final parsing/access.

This SDK PR is narrower: it prevents the SDK parser/accessor layer from leaking the raw Python iteration error for a Responses object with missing/null output. The Hermes recovery path remains useful even after this SDK fix, because a safe SDK may still surface an empty final output, and Hermes can then reconstruct the response from streamed deltas/items.

@iqdoctor
Copy link
Copy Markdown
Author

Update on downstream status: the canonical Hermes-side fix was merged via NousResearch/hermes-agent#32963, while the earlier linked #32884 was closed as a duplicate after the fix was cherry-picked/merged.

Current downstream evidence link:

NousResearch/hermes-agent#32963

That merged PR still demonstrates the same SDK-layer invariant issue: Hermes has to recover from streamed Codex Responses where the terminal/final Responses object has missing/null or empty output, and current SDK behavior can expose TypeError: 'NoneType' object is not iterable from the parser/accessor layer.

So the relationship is now:

@iqdoctor
Copy link
Copy Markdown
Author

Duplicate check update: I compared the newer duplicate PRs #3316 and #3317.

#3315 already includes the parser guard/regression and additionally covers Response.output_text, so I did not find any extra behavior from those PRs that needs to be pulled into this branch.

Responses objects can be constructed from malformed or partial payloads where output is None. Avoid leaking a raw Python TypeError from parser and output_text convenience paths; treat a missing output list as empty so callers receive stable SDK behavior.\n\nConstraint: observed downstream Hermes/Codex integrations received terminal Responses snapshots with output=None.\nRejected: Leaving applications to catch TypeError | the SDK owns the Responses object invariant and can handle the missing list at the iteration boundary.\nConfidence: medium\nScope-risk: narrow\nDirective: If maintainers prefer a typed validation error, preserve the invariant that raw 'NoneType' iteration never escapes.\nTested: uv run pytest -q tests/lib/responses/test_responses.py -> 7 passed\nNot-tested: Full repository test suite
@iqdoctor iqdoctor force-pushed the fix/responses-none-output-diagnostic branch from 2666857 to eed893c Compare May 27, 2026 02:59
@iqdoctor
Copy link
Copy Markdown
Author

Update: the automated Codex review on duplicate PRs #3316/#3317 caught one useful missing behavior, and I pulled it into this PR.

New coverage/fix in #3315:

  • ResponseStreamState now preserves the accumulated stream snapshot when the terminal response.completed.response.output is None or [], instead of replacing already-streamed output with an empty parsed response.
  • Added regression coverage for both terminal output=None and terminal output=[] after streamed text deltas.

Validation:

uv run pytest -q tests/lib/responses/test_responses.py
9 passed

So #3315 now covers:

  1. parse_response() null output guard
  2. Response.output_text null output guard
  3. streaming final-response preservation when terminal output is null/empty after prior deltas

Thanks to the review signal on #3316/#3317 for pointing out the stream-state data-loss risk.

@iqdoctor
Copy link
Copy Markdown
Author

Duplicate check update: new PR #3320 is another minimal parse_response() null-output guard. I compared the diff; it does not add behavior beyond #3315.

#3315 already includes the parser guard plus Response.output_text handling and stream-state preservation for terminal output=None / output=[] after prior streamed deltas.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant