GPTMap

openai-python 3.14.x and openai-node 7.16/7.17: Stream Error Normalization, WebSocket Backpressure, and SSE Fixes

Four OpenAI SDK releases in three days, one theme: reliability — Python normalizes stream errors into SDK exceptions, stringifies error codes, checks max_retries; Node caps WebSocket iterator backpressure and fixes lost terminal SSE events.

TL;DR
Four SDK releases (09-14 to 09-16) on reliability. python v3.14.x: stream errors become APITimeoutError/APIConnectionError (no auto-retry), error codes become strings, max_retries validated up front, commentary left unparsed by parse. node v7.16/7.17: maxBufferedEvents caps WebSocket iterator backpressure; terminal SSE events lacking a trailing blank line are flushed at EOF.
The openai SDK 2026-09 reliability wave is the group of four releases openai-python v3.14.0 / v3.14.1 and openai-node v7.16.0 / v7.17.0 (published 2026-09-14 through 09-16): the Python side normalizes transport exceptions during stream consumption into SDK exceptions, unifies error-code types, and tightens retry-parameter validation; the Node side adds a backpressure cap to WebSocket stream iterators, fixes lost terminal SSE events, and rejects function-backed API keys at WebSocket construction when no credential is available.

The official OpenAI SDKs shipped four releases in three days (2026-09-14 through 09-16), and unlike the recent wave of new API surfaces (prompt cache diagnostics, Live API, Agents API), this batch has a single theme: reliability. What exception does a mid-stream disconnect raise, what type is an error code, what happens when a WebSocket iterator falls behind, what if the server forgets to terminate the last SSE event — the exact problems production systems run into. This article traces every item to its commit, based on diffs and documentation extracted from the release tags on the day of writing.

1. Release timeline

PackageVersionReleased (UTC)Theme
openai-pythonv3.14.02026-09-14T23:28Stream error normalization (#3827) + string error codes
openai-pythonv3.14.12026-09-15T23:12max_retries pre-validation (#3867) + parse commentary exception
openai-nodev7.16.02026-09-15T16:48WebSocket iterator backpressure (#2748)
openai-nodev7.17.02026-09-16T19:23Compaction progress event (#2749) + SSE fix and WS credential checks

The two packages keep their non-matching version numbers, and one theme landing in different releases per package remains the norm; the rest of this article is grouped by package.

2. Python: stream consumption exceptions, normalized (#3827, v3.14.0)

This is the only feature-level Python change in the batch and the one with the widest production impact. Until now, transport-layer exceptions leaked out of Stream / AsyncStream consumption as raw httpx errors; from v3.14.0 they are normalized into SDK exceptions:

  • Read timeouts raise APITimeoutError;
  • Other httpx request failures raise APIConnectionError;
  • The original exception is always available as __cause__;
  • The README states it explicitly: stream consumption is not automatically retried — replaying a request could duplicate output already delivered to your application;
  • Compatibility boundary: the Assistants event-handler helpers and raw with_streaming_response iterators keep their existing behavior (the former unwrap SDK exceptions back to the legacy transport exceptions).

A stream consumption pattern adapted from the README's error-handling section:

import openai
from openai import OpenAI

client = OpenAI()

try:
    stream = client.responses.create(
        model="gpt-5.6-terra",
        input="Explain prompt caching to me",
        stream=True,
    )
    for event in stream:
        handle(event)  # persist your position so you can resume after a disconnect
except openai.APITimeoutError as e:
    print("read timeout, original exception:", e.__cause__)
except openai.APIConnectionError as e:
    print("connection failed, original exception:", e.__cause__)

Note the README's wording — catch these SDK exceptions instead of raw HTTPX exceptions. If your except clauses still name httpx exception classes, they will simply stop matching after the upgrade.

3. Python: string error codes, retry validation, and the parse exception (v3.14.0 / v3.14.1)

Error codes normalized to strings (#3532, v3.14.0). The code property of body-bearing API error objects (APIStatusError and its subclasses) changes from pass-through to always-str: 404 becomes '404', 0 becomes '0', the empty string stays empty, and None stays None (each case covered by the official tests). This is the quiet kind of breaking change — code comparing code to an integer will not throw, it will just never match:

except openai.APIStatusError as e:
    # from v3.14.0 on, e.code is always str or None
    if e.code == "insufficient_quota":  # string comparison
        ...

max_retries validated up front (#3867, v3.14.1). max_retries must be a non-negative integer: 0 disables retries; pass a large integer for a larger budget (the official test uses 10 to the power of 100); None raises TypeError (with a message suggesting 0 or a large integer) and negatives raise ValueError — all before the request is sent. The same commit tightens the retry loop itself: it now catches only transport request exceptions, so application exceptions raised by custom transports or hooks — including task-executor cancellation signals — propagate unchanged; the retry log line also gains a "retry i of N" progress indicator.

The parse commentary exception (#3861, v3.14.1). client.responses.parse(..., text_format=YourModel) used to attempt structured parsing on every text output item; now, among items carrying a phase marker, anything whose phase is not final_answer (commentary, for example) is left unparsed with parsed set to None — even when the text happens to match the schema — and a refusal does not cause commentary to be parsed as a fallback answer. output_text still concatenates all text including commentary, streamed text-done events follow the same rule, and outputs with a null phase keep the legacy behavior.

Other fixes in these releases (release-notes wording): bounded vector store file polling (#3401), normalized PathLike upload tuples (#3475), preserved response stream indexes after empty items (#3126), null-text handling in output_text (#3019), content filter errors now include the completion (#3094), and OPENAI_LOG gains warning / error / critical levels with invalid values ignored (#3734 — note it configures only the openai logger; configure HTTP transport loggers separately).

4. Node: maxBufferedEvents backpressure (#2748, v7.16.0)

Node-side WebSocket stream iterators previously buffered without bound — a slow consumer would eventually blow up memory or sink into backlog latency. v7.16.0 gives each iterator an independent backpressure cap, with the full semantics documented in docs/responses.md:

import OpenAI from 'openai';
import { ResponsesWS } from 'openai/resources/responses/ws';

const client = new OpenAI();
const socket = new ResponsesWS(client);

// Each iterator buffers independently; 256 is an example — size it to your processing capacity
for await (const event of socket.stream({ maxBufferedEvents: 256 })) {
  handle(event);
}

Semantics, straight from the documentation:

  • The count includes messages, raw data, errors, and lifecycle records (initial connection state, reconnecting, close);
  • When the next record would exceed the limit, that iterator discards its backlog, removes its listeners, and rejects further next() calls with a WebSocketError (message: WebSocket stream exceeded maxBufferedEvents (N)) — and a close record can overflow a full queue;
  • The shared socket and other iterators remain active; close the socket yourself when you no longer need it;
  • The limit persists across reconnects and does not restart a failed iterator;
  • It limits the event count, not payload bytes or total memory — one large message still counts as one record;
  • Omitting the option (including iterating over the socket directly) leaves buffering unlimited;
  • The option is also available on the beta Responses and Live WebSocket streams (the same commit touches the base classes of both);
  • Validation happens before listeners are attached: anything that is not a positive safe integer throws OpenAIError.

5. Node: SSE safety net and tighter WebSocket credentials (v7.17.0)

Terminal SSE events no longer dropped (#2726). SSE terminates an event with a blank line, but servers occasionally omit it after the final event — and the decoder used to swallow it, losing the terminating chunk that carries finish_reason. v7.17.0 adds flush() to the decoder: at end of stream, an in-progress event is emitted exactly once; records that already ended with a blank line are not delivered twice (flush returns null when nothing is in progress).

Function-backed API keys checked at WebSocket construction (#2586). For clients whose apiKey is a function (a key resolved per request), opening a WebSocket could previously fail after the connection was already established; now, if there is no resolved key (one resolved by a previous request and reusable) and no caller-supplied credential, construction throws before a socket is opened. The documented ways out: make a normal request first so the key resolves, pass a resolved Authorization header in the WebSocket options, use custom credential headers on compatible endpoints, or the Node ws transport's auth option.

Other items in this release (release-notes wording): chat runners preserve abort reasons (#2607), realtime preserves native WebSocket error causes (#2715), streaming runTools reject unfinished turns (#2716), zod strict schemas omit impossible optional branches (#2751), and the Responses API gains the compaction progress event (#2749 — covered in depth in its own article on this site). v7.16.0 also included: handling malformed WebSocket events and improved buffering (#2739), callback credentials kept local to each HTTP request (#2744), fallback abort subscriptions bounded with weak lifetimes (#2745), and linear-time trimming of Live transcript acknowledgment suffixes (#2740).

6. Upgrade notes: the behavior changes at a glance

ChangeWho hits itWhat to do
Stream exceptions changed type (#3827)streaming code whose except clauses name httpx exceptionsCatch APITimeoutError / APIConnectionError; build your own resume logic (the SDK will not retry a stream for you)
error.code is a string now (#3532)error handling comparing code to integersCompare against strings; the None branch is unchanged
max_retries validated earlier (#3867)callers passing None, negatives, or floats; tests pinning the old None error messageUse 0 to disable retries; catch the validation error at construction / with_options time
WS credential check earlier (#2586)function-backed apiKey clients opening WebSocketsResolve the key first or pass credentials explicitly (header / ws auth)

One sentence sums up the batch: move failures that used to explode mid-run to construction and request time, and turn leaked transport exceptions into programmable SDK exceptions — both steps toward production predictability.

7. Common pitfalls

  • A mid-stream disconnect no longer enters your old except branch: the exception types were normalized; switch your capture list to the SDK exceptions and inspect __cause__ when debugging.
  • An error-code comparison that is always false: code is a string now; integer comparisons must become string comparisons.
  • Constructing a client raises on max_retries: that is the new pre-request validation; check the argument type (a non-negative integer; use 0 to disable retries).
  • A WebSocketError from maxBufferedEvents: the socket is not down — the shared connection is alive, but that iterator's backlog overflowed and was shed deliberately. Raise the cap or consume faster; other iterators and the socket are unaffected.
  • Opening a WebSocket throws with a function-backed key client: credential resolution moved to construction time; supply credentials explicitly via one of the four routes in section 5.
  • No compaction progress event on the Python side: it has not landed as of v3.14.1 — see the cross-package section of our compaction article.

8. Next steps

Key points

  • python v3.14.0 (#3827, the headline): consuming a Stream / AsyncStream raises APITimeoutError on read timeouts and APIConnectionError on other httpx request failures, with the original exception on __cause__; the README states stream consumption is not automatically retried — replaying could duplicate output already delivered
  • python v3.14.0 (#3532): the code property of APIStatusError is normalized to strings — 404 becomes '404', 0 becomes '0', empty string and None stay as they are; code that compared code to integers must switch to string comparison
  • python v3.14.1 (#3867): max_retries is validated before a request is sent — must be a non-negative integer, 0 disables retries, None raises TypeError and negatives raise ValueError; the retry loop now catches only transport request exceptions, so application exceptions from custom transports or hooks propagate unchanged
  • python v3.14.1 (#3861): responses.parse leaves items whose phase is not final_answer (commentary) unparsed — parsed is always None even when the text happens to match the schema; output_text still concatenates all text
  • node v7.16.0 (#2748): socket.stream({ maxBufferedEvents: 256 }) caps each iterator's backlog independently; the count includes messages, raw data, errors, and lifecycle records; on overflow the iterator discards its backlog and rejects next() with WebSocketError while the shared socket and other iterators stay active; the limit persists across reconnects and counts events, not bytes
  • node v7.17.0 (#2726 / #2586): the SSE decoder flushes an in-progress event exactly once at EOF, so terminal events no longer vanish when the server omits the trailing blank line; function-backed API key clients without a resolved key or caller-supplied credential throw at WebSocket construction, before a socket is opened

Frequently asked questions

openai-python v3.14.0 (2026-09-14) is headlined by stream error normalization (#3827), plus string error codes and OPENAI_LOG extensions; v3.14.1 (09-15) adds max_retries pre-validation and the parse commentary exception. openai-node v7.16.0 (09-15) adds WebSocket iterator backpressure via maxBufferedEvents (#2748); v7.17.0 (09-16) adds the compaction progress event, the SSE terminal-event fix, and tighter WebSocket credential checks.

Official references

Related articles

Subscribe to GPTMap Weekly

One email every Monday: curated OpenAI updates, deep dives, and best practices. No ads, unsubscribe anytime.

Submitting opens Buttondown in a new tab to confirm your subscription.

GPTMap EditorialPublished 2026-09-17 10 min read
Test environment (EEAT)
Last tested: 2026-09-17
Model used: openai-python v3.14.0 / v3.14.1; openai-node v7.16.0 / v7.17.0 (SDK behavior layer, model-agnostic)