diff --git a/src/openai/types/responses/response.py b/src/openai/types/responses/response.py index 92c02ce69f..abc7d41e28 100644 --- a/src/openai/types/responses/response.py +++ b/src/openai/types/responses/response.py @@ -551,10 +551,10 @@ def output_text(self) -> str: If no `output_text` content blocks exist, then an empty string is returned. """ texts: List[str] = [] - for output in self.output: + for output in self.output or []: 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: # pyright: ignore[reportUnnecessaryComparison] texts.append(content.text) return "".join(texts) diff --git a/tests/lib/responses/test_null_output.py b/tests/lib/responses/test_null_output.py index 4c782de03a..ce8aaea974 100644 --- a/tests/lib/responses/test_null_output.py +++ b/tests/lib/responses/test_null_output.py @@ -8,6 +8,7 @@ from openai import OpenAI, AsyncOpenAI from openai.types.responses import ToolParam +from openai.types.responses.response import Response class Answer(BaseModel): @@ -123,3 +124,55 @@ async def test_stream_recovers_finalized_output(sync: bool, terminal_output: str assert tool.type == "function_call" and tool.status == "completed" assert tool.id == "fc_test" assert tool.parsed_arguments == {"answer": 4} + + +def _make_response(**overrides: object) -> Response: + base: dict[str, object] = dict( + id="resp_test", + created_at=0, + model="gpt-5.2", + object="response", + parallel_tool_calls=False, + tool_choice="auto", + tools=[], + ) + base.update(overrides) + return Response.model_construct(**base) + + +def test_output_text_with_null_output() -> None: + """output_text must not crash when output is None, matching parse_response's own guard.""" + response = _make_response(output=None) + assert response.output_text == "" + + +def test_output_text_with_missing_output_text() -> None: + """A message content block whose type is output_text but whose text is None (a + partial/aborted item) must be skipped rather than appended as None.""" + response = _make_response( + output=[ + { + "id": "msg_test", + "type": "message", + "role": "assistant", + "status": "in_progress", + "content": [{"type": "output_text", "text": None, "annotations": []}], + } + ] + ) + assert response.output_text == "" + + +def test_output_text_with_present_output() -> None: + response = _make_response( + output=[ + { + "id": "msg_test", + "type": "message", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "hello", "annotations": []}], + } + ] + ) + assert response.output_text == "hello"