Skip to content

fix: guard None refusal in ItemHelpers.extract_last_content#3428

Open
rmotgi1227 wants to merge 1 commit into
openai:mainfrom
rmotgi1227:fix/guard-none-refusal-extract-last-content
Open

fix: guard None refusal in ItemHelpers.extract_last_content#3428
rmotgi1227 wants to merge 1 commit into
openai:mainfrom
rmotgi1227:fix/guard-none-refusal-extract-last-content

Conversation

@rmotgi1227
Copy link
Copy Markdown

Summary

PR #3394 added an or \"\" guard for ResponseOutputText.text in extract_last_content but left the ResponseOutputRefusal branch unguarded:

# Fixed by #3394 ✓
if isinstance(last_content, ResponseOutputText):
    return last_content.text or ""

# Still unguarded before this PR ✗
elif isinstance(last_content, ResponseOutputRefusal):
    return last_content.refusal        # can be None in practice

ResponseOutputRefusal.refusal is typed as str per the Responses API schema, but the same provider gateways (e.g. LiteLLM) and model_construct paths during streaming that surface None for .text can also surface None for .refusal. Without the guard, callers that rely on the -> str return type of extract_last_content receive None and may crash downstream.

The fix adds the same or "" coercion to the refusal branch, matching the precedent from #3394 for text and extract_refusal (which already uses content_item.refusal or "" at items.py:739).

Test plan

Added two regression tests in tests/utils/test_pretty_print_and_items.py mirroring the structure of the tests added in #3394:

  • test_extract_last_content_returns_empty_string_for_none_refusal — verifies the method returns "" (not None) when ResponseOutputRefusal.refusal is None
  • test_extract_last_content_returns_refusal_normally — verifies unaffected happy-path behaviour
uv run pytest tests/utils/test_pretty_print_and_items.py tests/test_items_helpers.py -v
# 43 passed
make lint  # All checks passed
make typecheck  # Success: no issues found

Issue number

Discovered while auditing for the same guard-None pattern that #3394 and #3375 fixed.

Checks

  • I've added new tests (if relevant)
  • I've added/updated the relevant documentation (no doc changes needed)
  • I've run make lint and make format
  • I've made sure tests pass

PR openai#3394 added ``or ""`` for ``ResponseOutputText.text`` in the
``extract_last_content`` method but left the ``ResponseOutputRefusal``
branch unguarded.  ``ResponseOutputRefusal.refusal`` is typed as ``str``
per the Responses API schema, but the same provider gateways (e.g.
LiteLLM) and ``model_construct`` paths during streaming that surface
``None`` for ``.text`` can also surface ``None`` for ``.refusal``.
Without the guard, callers that rely on the ``-> str`` return type
receive ``None`` and may crash downstream.

Add the same ``or ""`` coercion to the refusal branch and add regression
tests mirroring the ones added for the text branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@seratch seratch added the duplicate This issue or pull request already exists label May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

duplicate This issue or pull request already exists

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants