Skip to content

fix(examples): avoid unhandled WebSocket rejections - #2669

Merged
HAYDEN-OAI merged 1 commit into
mainfrom
codex/websocket-example-error-ownership
Sep 7, 2026
Merged

fix(examples): avoid unhandled WebSocket rejections#2669
HAYDEN-OAI merged 1 commit into
mainfrom
codex/websocket-example-error-ownership

Conversation

@HAYDEN-OAI

Copy link
Copy Markdown
Contributor

Summary

  • Use the SDK's existing connecting-state send queue instead of a separate raw-socket opening waiter, so the example's response/error/close handlers are installed before connection events can arrive.
  • Let the existing SDK error callback own protocol errors, rather than rejecting and removing that callback during generic event dispatch.
  • Delete 48 example lines without changing the SDK, generated code, parsers, tools, models, or dependencies.

Reproduction

For an API error frame, the SDK emits the generic event callback before its dedicated error callback. The example previously handled the generic event and removed its error listener, so the SDK then produced an unhandled rejection for the same failure. Opening handshake failures and an error frame delivered together with HTTP 101 exposed related listener gaps.

These cases already exited with status 1; the bug was the additional uncaught rejection and crash diagnostics. The fix retains normal failure exit codes, API-error precedence over a subsequent close, the six-request tool loop, and response-ID chaining. The first demo heading can now print while the connection is opening.

Validation

  • Extend the existing real-WebSocket fixture to ten cases. Seven regressions fail before the source change, while the three original successful/early-close controls pass. All ten pass afterward on exact Node 22.0.0, Node 24.19.0, and Node 26.7.0.
  • The fixture observes uncaughtExceptionMonitor without handling exceptions or changing Node's default exit behavior. It covers flat/nested API errors, immediate closes, rejected upgrades, and HTTP 101 coalesced with an initial error frame.
  • Independent checks execute the actual example against the freshly built public SDK: all 13 scenarios pass on each of those three runtimes, with zero uncaught failures. Before the fix, eight scenarios exhibited unhandled rejections; normal request counts and exit-code controls still passed.
  • Full canonical handwritten suite: 7,890 tests pass across 208 files.
  • Canonical build, formatting/lint, TypeScript 6, and whitespace checks pass. Independent adversarial review found no remaining issues.

The generated suite and remaining ecosystem/packaging checks run in CI.

@HAYDEN-OAI
HAYDEN-OAI requested a review from a team as a code owner September 7, 2026 00:39
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-07T00:41:32.571649Z 09444de PR opened
🔒 Security Review Completed 2026-09-07T00:41:51.312505Z 09444de PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

32 mixed files remain; 0 existing customizations changed.

Compared fdb038e2150909444de4d123. Generated baselines verified.

32 existing customizations unchanged
  • api.md
  • scripts/castiron/README.md
  • scripts/castiron/custom_code_report.py
  • scripts/castiron/test_custom_code_report.py
  • src/client.ts
  • src/resources/audio/transcriptions.ts
  • src/resources/audio/translations.ts
  • src/resources/beta/assistants.ts
  • src/resources/beta/beta.ts
  • src/resources/beta/index.ts
  • src/resources/beta/responses/internal-base.ts
  • src/resources/beta/responses/responses.ts
  • src/resources/beta/threads/index.ts
  • src/resources/beta/threads/runs/index.ts
  • src/resources/beta/threads/runs/runs.ts
  • src/resources/beta/threads/threads.ts
  • src/resources/chat/completions/completions.ts
  • src/resources/chat/completions/index.ts
  • src/resources/conversations/index.ts
  • src/resources/embeddings.ts
  • src/resources/files.ts
  • src/resources/fine-tuning/checkpoints/permissions.ts
  • src/resources/images.ts
  • src/resources/responses/internal-base.ts
  • src/resources/responses/responses.ts
  • src/resources/skills/skills.ts
  • src/resources/skills/versions/versions.ts
  • src/resources/vector-stores/file-batches.ts
  • src/resources/vector-stores/files.ts
  • src/resources/webhooks/index.ts
  • src/resources/webhooks/webhooks.ts
  • tests/lib/data-residency.test.ts

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 34070537085 --repo openai/openai-node \
  --name castiron-custom-code-34070537085-1 --dir /tmp/castiron-custom-code-34070537085-1
git apply --stat /tmp/castiron-custom-code-34070537085-1/custom-code.patch
cat /tmp/castiron-custom-code-34070537085-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin fdb038e21509264bcdf81741a40988dd694188b1 09444de4d12396d2392c99b1a5ba64e35cd84f23
python3 scripts/castiron/custom_code_report.py report \
  --base fdb038e21509264bcdf81741a40988dd694188b1 \
  --head 09444de4d12396d2392c99b1a5ba64e35cd84f23 --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-09444de4d123
cat /tmp/castiron-custom-code-09444de4d123/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

@openai-sdks

openai-sdks Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

OkTest Summary

237/237 SDK tests passed in 10.594s for Node SDK PR #2669.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 221ms
tests/chat-completions-create.test.ts ✅ Passed 217ms
tests/chat-completions-stream.test.ts ✅ Passed 222ms
tests/files-content-binary.test.ts ✅ Passed 158ms
tests/files-create-multipart.test.ts ✅ Passed 153ms
tests/files-list-pagination.test.ts ✅ Passed 157ms
tests/initialize-config.test.ts ✅ Passed 186ms
tests/instance-isolation.test.ts ✅ Passed 103ms
tests/models-list.test.ts ✅ Passed 139ms
tests/responses-background-lifecycle.test.ts ✅ Passed 223ms
tests/responses-body-method-errors.test.ts ✅ Passed 304ms
tests/responses-cancel-timeout.test.ts ✅ Passed 194ms
tests/responses-cancel.test.ts ✅ Passed 227ms
tests/responses-compact-retries.test.ts ✅ Passed 219ms
tests/responses-compact.test.ts ✅ Passed 208ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 141ms
tests/responses-create-advanced.test.ts ✅ Passed 207ms
tests/responses-create-disconnect.test.ts ✅ Passed 1.18s
tests/responses-create-errors.test.ts ✅ Passed 201ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 133ms
tests/responses-create-retries.test.ts ✅ Passed 287ms
tests/responses-create-stream-failures.test.ts ✅ Passed 216ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 2.152s
tests/responses-create-stream-wire.test.ts ✅ Passed 2.613s
tests/responses-create-stream.test.ts ✅ Passed 106ms
tests/responses-create-terminal-states.test.ts ✅ Passed 267ms
tests/responses-create-timeout.test.ts ✅ Passed 210ms
tests/responses-create.test.ts ✅ Passed 138ms
tests/responses-delete.test.ts ✅ Passed 261ms
tests/responses-input-items-errors.test.ts ✅ Passed 287ms
tests/responses-input-items-list.test.ts ✅ Passed 198ms
tests/responses-input-items-options.test.ts ✅ Passed 129ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 250ms
tests/responses-input-tokens-count.test.ts ✅ Passed 240ms
tests/responses-malformed-inputs.test.ts ✅ Passed 2.215s
tests/responses-not-found-errors.test.ts ✅ Passed 237ms
tests/responses-parse.test.ts ✅ Passed 187ms
tests/responses-retrieve-retries.test.ts ✅ Passed 369ms
tests/responses-retrieve.test.ts ✅ Passed 250ms
tests/responses-stored-method-errors.test.ts ✅ Passed 608ms
tests/retry-behavior.test.ts ✅ Passed 2.926s
tests/sdk-error-shape.test.ts ✅ Passed 287ms

View OkTest run #34070518718

SDK merge (fc0e79a031af) · head (09444de4d123) · base (fdb038e21509) · OkTest (2b1bdfd25e98)

@markstuart-oai markstuart-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed both changed files and the SDK send queue, generic/dedicated error dispatch, adapter/emitter lifecycle, and tool-loop callers. No actionable correctness, security, or maintainability findings. This is a useful structural simplification: the canonical queue removes the raw opening waiter, and the dedicated SDK error callback retains ownership until dispatch. Printing the first heading while connecting is reasonable.

Independent validation: 124 focused tests passed; the ten-case example fixture also passed on Node 22.0.0 and 26.7.0 (primary run: 24.20.0). Running the new fixture against base reproduced seven failures with all three controls passing. A separately written real-loopback fixture exercised the actual transpiled example through freshly built public CJS and ESM entrypoints: 78 head runs passed across those three runtimes, with zero uncaught errors, one failure report per failing scenario, preserved error-over-close precedence, six-request response-ID/tool-output chaining, and owned child/server cleanup. Another 78 base runs reproduced the seven targeted unhandled-rejection scenarios. This includes rejected upgrades and a single transport write containing HTTP 101 plus the initial error frame.

Canonical build/lint, TypeScript 6, and whitespace checks passed. Registry installation was unavailable, so validation used a private copy of cached dependencies with the matching lockfile. I did not rerun the full handwritten/generated or ecosystem suites.

@HAYDEN-OAI
HAYDEN-OAI added this pull request to the merge queue Sep 7, 2026
@HAYDEN-OAI
HAYDEN-OAI removed this pull request from the merge queue due to a manual request Sep 7, 2026

@jbeckwith-oai jbeckwith-oai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed exact head 09444de. Removing the example-local opening waiter and manual error branch correctly leaves socket-open queuing and API/transport error ownership with the SDK connection. The regression covers rejected upgrades, nested/flat API errors, same-write handshake/error delivery, close precedence, multi-turn chaining, and uncaught rejection monitoring. CI is green and there are no unresolved threads. No in-scope findings.

@HAYDEN-OAI
HAYDEN-OAI added this pull request to the merge queue Sep 7, 2026
Merged via the queue into main with commit 744b0ad Sep 7, 2026
38 checks passed
@HAYDEN-OAI
HAYDEN-OAI deleted the codex/websocket-example-error-ownership branch September 7, 2026 18:09
@openai-sdks openai-sdks Bot mentioned this pull request Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants