Skip to content

fix: sanitize newlines in NO_PROXY env var before httpx client init#3519

Open
rkfshakti wants to merge 4 commits into
openai:mainfrom
rkfshakti:fix/no-proxy-newline-sanitize
Open

fix: sanitize newlines in NO_PROXY env var before httpx client init#3519
rkfshakti wants to merge 4 commits into
openai:mainfrom
rkfshakti:fix/no-proxy-newline-sanitize

Conversation

@rkfshakti

Copy link
Copy Markdown
  • fix(parsing): guard against None response.output in parse_response
  • fix(streaming): preserve accumulated output when response.completed has null output
  • fix(streaming): route null-output fallback through parse_response
  • fix(client): sanitize newlines in NO_PROXY env var before httpx client init

The chatgpt.com Codex backend sometimes sends response.output: null in
the consolidated response.completed event, even when valid output_item.done
events were streamed earlier. The SDK then raises TypeError: 'NoneType'
object is not iterable inside the stream accumulator, killing the entire
stream before the consumer can read the deltas.

Fix: iterate over response.output or [] instead of response.output directly.

Closes openai#3325
…as null output

The chatgpt.com Codex backend sometimes sends response.output: null in the
consolidated response.completed event even when valid output_item.done events
were streamed earlier (issue openai#3325). The previous fix (response.output or [])
prevented the TypeError but discarded the already-streamed snapshot.output,
causing the final ParsedResponse to have empty output/output_text.

Move the guard into ResponseStreamState.accumulate_event: when
event.response.output is None and the snapshot has accumulated output items,
build the completed response from the snapshot instead of calling
parse_response with an empty output list. This preserves streamed text and
tool calls in get_final_response() and ResponseCompletedEvent.

Addresses Codex review feedback on openai#3517.
Address Codex P2 review feedback on the previous commit (1c050f6):

1. Run response parsing on the streamed fallback: instead of building
   ParsedResponse directly from snapshot.output via construct_type_unchecked
   (which left output_parsed/parsed_arguments as None), inject the streamed
   items into a shallow copy of the response and pass it through parse_response
   so text_format and parsed_arguments logic still runs.

2. Handle null completed output without streamed items: re-add the
   'response.output or []' guard in parse_response() so a null-output
   response.completed with no prior output_item.added events returns a parsed
   response with an empty output list instead of raising TypeError.

Together these cover both branches: null output WITH accumulated snapshot items
(parse_response runs on the injected items) and null output WITHOUT items
(parse_response returns empty output gracefully).
…t init

httpx's get_environment_proxies() only splits NO_PROXY by comma, not by
newline. When NO_PROXY contains newline characters (common in Docker
environments, .env files, or shell scripts), the newline becomes part of
the hostname and httpx raises InvalidURL.

Add _sanitize_no_proxy() which replaces newlines with commas and strips
whitespace, called from _DefaultHttpxClient.__init__() before the httpx
client is constructed.

Closes openai#3303.
@rkfshakti
rkfshakti requested a review from a team as a code owner July 20, 2026 15:19

@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: 217dc74b35

ℹ️ 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".

# only splits by comma, not by newline. When NO_PROXY contains newlines
# (common in Docker/.env files), the newline becomes part of the hostname
# and httpx raises InvalidURL. See issue #3303.
_sanitize_no_proxy()

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 Sanitize NO_PROXY before async client init

This call only runs in _DefaultHttpxClient; AsyncOpenAI creates AsyncHttpxClientWrapper from _DefaultAsyncHttpxClient (src/openai/_base_client.py:1546, 1441-1446), whose constructor still enters httpx.AsyncClient.__init__ with the original environment. In async clients with NO_PROXY/no_proxy containing newlines, the same InvalidURL during httpx environment proxy parsing remains, so the fix only works for sync users.

Useful? React with 👍 / 👎.

type_=type(event.response),
value={
**event.response.to_dict(),
"output": [item.to_dict() for item in snapshot.output],

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 Use output_item.done data for null-output fallback

When response.completed has output: null, this fallback copies snapshot.output, but the snapshot is populated from response.output_item.added and deltas and never replaced from response.output_item.done before this branch. For streams where the added item is still in_progress and the done event carries the completed item/final annotations, the final parsed response returned here preserves the stale in-progress item instead of the finalized output.

Useful? React with 👍 / 👎.

becomes part of the hostname and httpx raises ``InvalidURL`` (issue #3303).
"""
for key in ("NO_PROXY", "no_proxy"):
val = os.environ.get(key)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Import os before using it in sanitizer

With the default sync client, _DefaultHttpxClient.__init__ now always calls _sanitize_no_proxy(), and this line references os even though _base_client.py does not import it. Any OpenAI()/DefaultHttpxClient() construction therefore raises NameError before httpx initialization, even when NO_PROXY is unset.

Useful? React with 👍 / 👎.

@rkfshakti rkfshakti changed the title fix/no proxy newline sanitize fix: sanitize newlines in NO_PROXY env var before httpx client init Jul 20, 2026
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