From 9018ec5e27683eec8b9c3753774f7ecf1788bbf3 Mon Sep 17 00:00:00 2001 From: okxint Date: Sun, 5 Jul 2026 10:23:31 +0530 Subject: [PATCH 1/2] fix(responses): guard against null text in Response.output_text property Filters out content items where `text` is None before joining, preventing a TypeError crash when the API returns null for a text block (issue #3063). --- src/openai/types/responses/response.py | 2 +- tests/lib/responses/test_responses.py | 74 ++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 67102d2628..e7c53dd0f4 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -447,7 +447,7 @@ def output_text(self) -> str: for output in self.output: if output.type == "message": for content in output.content: - if content.type == "output_text": + if content.type == "output_text" and content.text is not None: texts.append(content.text) return "".join(texts) diff --git a/tests/lib/responses/test_responses.py b/tests/lib/responses/test_responses.py index 8e5f16df95..19e1f4637a 100644 --- a/tests/lib/responses/test_responses.py +++ b/tests/lib/responses/test_responses.py @@ -8,6 +8,9 @@ from openai import OpenAI, AsyncOpenAI from openai._utils import assert_signatures_in_sync +from openai.types.responses.response import Response +from openai.types.responses.response_output_message import ResponseOutputMessage +from openai.types.responses.response_output_text import ResponseOutputText from ...conftest import base_url from ..snapshots import make_snapshot_request @@ -41,6 +44,77 @@ def test_output_text(client: OpenAI, respx_mock: MockRouter) -> None: ) +def _make_response_with_text(text: str | None) -> Response: + """Build a minimal Response via model_construct, bypassing validation to simulate + raw API payloads where `text` may be null.""" + content_block = ResponseOutputText.model_construct( + type="output_text", + annotations=[], + logprobs=None, + text=text, + ) + message = ResponseOutputMessage.model_construct( + id="msg_test", + type="message", + status="completed", + role="assistant", + content=[content_block], + ) + return Response.model_construct( + id="resp_test", + object="response", + created_at=0, + status="completed", + model="gpt-4o-mini", + output=[message], + parallel_tool_calls=True, + text=None, + tool_choice="auto", + tools=[], + truncation="disabled", + ) + + +def test_output_text_null_guard() -> None: + """output_text must not crash when content items have text=None (issue #3063).""" + # Single null text block -> empty string + response = _make_response_with_text(None) + assert response.output_text == "" + + # Valid text block -> the text is returned + response = _make_response_with_text("hello") + assert response.output_text == "hello" + + # Mixed: null and valid in the same message -> only valid text joined + null_block = ResponseOutputText.model_construct( + type="output_text", annotations=[], logprobs=None, text=None + ) + valid_block = ResponseOutputText.model_construct( + type="output_text", annotations=[], logprobs=None, text="world" + ) + message = ResponseOutputMessage.model_construct( + id="msg_mixed", + type="message", + status="completed", + role="assistant", + content=[null_block, valid_block], + ) + response = Response.model_construct( + id="resp_mixed", + object="response", + created_at=0, + status="completed", + model="gpt-4o-mini", + output=[message], + parallel_tool_calls=True, + text=None, + tool_choice="auto", + tools=[], + truncation="disabled", + ) + assert response.output_text == "world" + + @pytest.mark.parametrize("sync", [True, False], ids=["sync", "async"]) def test_stream_method_definition_in_sync(sync: bool, client: OpenAI, async_client: AsyncOpenAI) -> None: checking_client: OpenAI | AsyncOpenAI = client if sync else async_client From 91f9172b377cf1fe48ef70a11acf6271647001af Mon Sep 17 00:00:00 2001 From: okxint Date: Fri, 31 Jul 2026 12:13:39 +0530 Subject: [PATCH 2/2] fix(types): make ImageGenerationCall result and status optional result (base64 image string) and status are output-only fields populated by the API response. Marking them Required on an input TypedDict forces callers to provide values they don't have and wouldn't supply when referencing an existing image generation call by id and type. Fixes #2648 --- src/openai/types/responses/response_input_item_param.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openai/types/responses/response_input_item_param.py b/src/openai/types/responses/response_input_item_param.py index 0b0d68cdbd..e888ff879d 100644 --- a/src/openai/types/responses/response_input_item_param.py +++ b/src/openai/types/responses/response_input_item_param.py @@ -193,10 +193,10 @@ class ImageGenerationCall(TypedDict, total=False): id: Required[str] """The unique ID of the image generation call.""" - result: Required[Optional[str]] + result: Optional[str] """The generated image encoded in base64.""" - status: Required[Literal["in_progress", "completed", "generating", "failed"]] + status: Literal["in_progress", "completed", "generating", "failed"] """The status of the image generation call.""" type: Required[Literal["image_generation_call"]]