Skip to content

fix: handle null text in output_text items#3454

Open
C1-BA-B1-F3 wants to merge 7 commits into
openai:mainfrom
C1-BA-B1-F3:fix/output-text-null-text
Open

fix: handle null text in output_text items#3454
C1-BA-B1-F3 wants to merge 7 commits into
openai:mainfrom
C1-BA-B1-F3:fix/output-text-null-text

Conversation

@C1-BA-B1-F3

Copy link
Copy Markdown

Summary

When the Responses API returns output_text items with text: null, the SDK currently crashes with TypeError. This fix adds null checks to handle this gracefully.

Changes

  1. response.py - output_text property: Now skips null text values instead of appending None to the texts list
  2. _parsing/_responses.py - parse_response: Sets parsed=None for null text items instead of crashing when trying to parse
  3. _parsing/_responses.py - parse_text: Handles text_format=None correctly (treats it as "don't parse")

Reproduction

from openai.types.responses.response import Response

data = {
    "id": "resp_test",
    "object": "response",
    "created_at": 0,
    "status": "completed",
    "model": "gpt-4o-mini",
    "output": [{
        "id": "msg_test",
        "type": "message",
        "status": "completed",
        "role": "assistant",
        "content": [
            {"type": "output_text", "annotations": [], "logprobs": [], "text": None},
            {"type": "output_text", "annotations": [], "logprobs": [], "text": "Hello"},
        ],
    }],
    "parallel_tool_calls": True,
    "tool_choice": "auto",
    "tools": [],
}

response = Response.model_validate(data)
print(response.output_text)  # Before: TypeError, After: "Hello"

Tests

Added two new tests:

  • test_output_text_with_null_text: Verifies output_text property handles null text
  • test_parse_response_with_null_text: Verifies parse_response handles null text items

Fixes #3063

Guard against empty type args when using bare dict or list annotations
(no type parameters, e.g.  instead of ).

Previously:
- construct_type() raised ValueError on bare dict
- construct_type() raised IndexError on bare list
- _transform_recursive() raised IndexError on bare dict

Now these cases gracefully return the data as-is, matching the behavior
of parameterized types when the data already matches.

Fixes openai#3338, openai#3341
…I responses

Fixes openai#3179

The API can return null for the action field in web search calls (e.g.,
when the search is still in progress or the action hasn't been
determined yet). This change makes the action field Optional[Action]
with a default of None, matching the actual API behavior.

This allows users to safely check action.type without getting an
AttributeError when action is None.
When NO_PROXY or other proxy environment variables contain newline
characters (common in Docker, .env files, or shell scripts), httpx's
get_environment_proxies() only splits by comma and fails with InvalidURL.

This fix adds sanitize_proxy_env_vars() that removes newlines from proxy
environment variables before httpx reads them.

Fixes openai#3303
The ActionSearchSource type only supported type='url' with a required url
field, but the API returns specialized data sources (weather, sports,
finance) with type='api', a name field, and url=None.

Changes:
- Add 'api' to the type Literal in both response and param types
- Make url optional (absent when type='api')
- Add optional name field for the data source identifier

Fixes openai#2736
The error event check inside the `if sse.event.startswith("thread.")` block
was unreachable because "error" never starts with "thread.". This meant
error events from the SSE stream were silently ignored in the thread event
path, instead of raising an APIError.

Fix: Move error event handling to the top of the event loop, before the
thread.* check, so error events are always handled regardless of context.

Fixes openai#2796
When response.output is None (e.g., from Codex backend), parse_response()
would crash with TypeError: 'NoneType' object is not iterable.

This fix adds a defensive check to treat None output as an empty list,
preventing the crash while maintaining backward compatibility.

Fixes openai#3312, openai#3313, openai#3314, openai#3321
When the API returns output_text items with text: null, the SDK now
handles them gracefully instead of raising TypeError.

Changes:
- output_text property skips null text values instead of appending None
- parse_response sets parsed=None for null text items instead of crashing
- parse_text handles text_format=None correctly

Fixes openai#3063
@C1-BA-B1-F3 C1-BA-B1-F3 requested a review from a team as a code owner June 26, 2026 02:39

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a17d00276

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/openai/_streaming.py
# we have to special case the Assistants `thread.` events since we won't have an "event" key in the data
if sse.event and sse.event.startswith("thread."):
# Handle error events first - these can occur outside of thread.* events
if sse.event == "error":

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Assistants error payloads

When a beta Assistants stream emits its generated ErrorEvent, the SSE frame uses event: error with the ErrorObject fields (message, type, etc.) at the top level of the data, not nested under error. This new catch-all path therefore raises a generic APIError before the synthesized-event path can cast and deliver that ErrorEvent to AssistantEventHandler.on_event, so users of threads/runs streaming lose the actual server message and any custom error-event handling. Please either handle the top-level error-object shape here or let synthesized streams process those events as before.

Useful? React with 👍 / 👎.

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.

Response.output_text and responses.parse fail when an output_text item has text: null

1 participant