Repository navigation
fix(examples): reject incomplete multi-agent streams - #2666
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Castiron custom code✅ No new custom-code files detected. 32 mixed files remain; 0 existing customizations changed. Compared 32 existing customizations unchanged
A changed generated baseline means this report cannot reliably identify which handwritten lines changed. Inspect the custom-code diffDownload the exact patch produced by this run (requires repository access): gh run download 34068029038 --repo openai/openai-node \
--name castiron-custom-code-34068029038-1 --dir /tmp/castiron-custom-code-34068029038-1
git apply --stat /tmp/castiron-custom-code-34068029038-1/custom-code.patch
cat /tmp/castiron-custom-code-34068029038-1/custom-code.patchOr reproduce it from an SDK checkout containing the vendored reporter: git fetch --no-tags origin 7b5d1209090f82f56b4971976147b5c2a859452e 5d64d68922760ed420d35657c1f6a023ac85062c
python3 scripts/castiron/custom_code_report.py report \
--base 7b5d1209090f82f56b4971976147b5c2a859452e \
--head 5d64d68922760ed420d35657c1f6a023ac85062c --fetch --require-head-hash --public \
--out /tmp/castiron-custom-code-5d64d6892276
cat /tmp/castiron-custom-code-5d64d6892276/custom-code.patchThis is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR. |
jbeckwith-oai
left a comment
There was a problem hiding this comment.
Reviewed exact head 5d64d68.\n\nThe example now requires an explicit coordinator response.completed event before treating normal iterator termination—whether [DONE] or EOF—as success. Child completion/failure/incomplete events remain nonterminal, and the loop deliberately continues after coordinator completion so later root failures, named SSE errors, malformed frames, or transport errors retain precedence rather than being hidden.\n\nThe new terminal error is generic and does not surface response payload or private failure detail. Root ownership handling remains aligned with the existing failed/incomplete logic (omitted, null, or explicit /root).\n\nVerified locally at the exact head:\n- focused executable multi-agent SSE suite: 25/25 passed\n- oxlint passed for both changed files\n- oxfmt check passed\n- git diff --check passed\n\nNo unresolved review threads and no in-scope blocking findings. Hosted Node 22/26 and ecosystem jobs were still running at submission; all completed checks were green or neutral.
markstuart-oai
left a comment
There was a problem hiding this comment.
Reviewed exact head 5d64d68 against base/merge-base 7b5d120. No actionable correctness, security, or maintainability findings.
The coordinator-completion bit is the minimal state needed to reject incomplete EOF/[DONE] without breaking early and hiding later errors. Ownership matches the existing omitted/null/explicit /root contract; child terminal events remain nonterminal. This policy belongs in the example, not the shared SSE parser. Growth is proportionate: example 44→50 lines; existing focused test file 179→223.
Independent validation:
- Actual executable example: 25/25 tests on Node 22.0.0 and 24.20.0.
- Existing SSE sentinel/error/privacy/early-exit suites: 83/83 tests.
- 28 synthetic source-execution cases against built CommonJS and ESM SDKs on Node 22.0.0, 24.20.0, and 26.7.0, including ten base-negative controls, later root failures, malformed/named errors, transport-error identity, cleanup, partial output, child continuation, and preserved sentinel behavior.
- Build, focused type check, changed-file Oxlint/Oxfmt, and diff whitespace check passed.
All fixtures were local/synthetic. Whole-project type checking was limited by missing optional Azure/Express example dependencies; I did not rerun the full handwritten/generated suites or independently compare base/head build bytes. SDK source and dependencies are unchanged.
Workspace: full clone; corrected detached worktree created at freshly fetched default main@518756a59fa9693c0751fb91403d017cfcd0037a, then checked out the exact PR head. Refreshed open/non-draft state, unchanged head, discussion, and complete review history immediately before submission; no prior Mark outcome or hold.
Automated Release PR --- ## [7.11.0](openai/openai-node@v7.10.0...v7.11.0) (2026-09-08) ### Features * **api:** Add prompt cache diagnostics ([openai#2608](openai#2608)) ([f83de8f](openai@f83de8f)) * **api:** add service-account API key expiration fields ([openai#2618](openai#2618)) ([eecbebe](openai@eecbebe)) * **api:** correct function argument completion event fields (openapi-545) ([openai#2611](openai#2611)) ([31bf7e7](openai@31bf7e7)) * **api:** recognize incomplete web search call status ([openai#2576](openai#2576)) ([c093c44](openai@c093c44)) ### Bug Fixes * **assistants:** retain terminal message snapshots ([openai#2650](openai#2650)) ([414677e](openai@414677e)) * **audio:** only signal started recording processes ([openai#2665](openai#2665)) ([cebdf7c](openai@cebdf7c)) * **audio:** prefer readable inputs over body metadata ([openai#2548](openai#2548)) ([86ff7d5](openai@86ff7d5)) * **audio:** use ALSA card identifiers when recording ([openai#2564](openai#2564)) ([67da19c](openai@67da19c)) * **audio:** wait for input cleanup before reporting success ([openai#2624](openai#2624)) ([5ed87fe](openai@5ed87fe)) * **auth:** account for token exchange elapsed time ([openai#2609](openai#2609)) ([b0f6f20](openai@b0f6f20)) * **auth:** validate Azure metadata access token values ([openai#2523](openai#2523)) ([32b4d35](openai@32b4d35)) * **azure:** carry declared options through withOptions and fix deployment paths ([openai#2514](openai#2514)) ([f8f29fb](openai@f8f29fb)) * **bedrock:** cancel requests awaiting AWS credentials ([openai#2610](openai#2610)) ([97c94e4](openai@97c94e4)) * bound partial number recovery to its token ([openai#2692](openai#2692)) ([0d306ca](openai@0d306ca)) * **build:** fail on reported transpile errors ([openai#2638](openai#2638)) ([833e3e8](openai@833e3e8)) * **build:** fail when a compiler worker is signaled ([openai#2633](openai#2633)) ([cd79cd1](openai@cd79cd1)) * **build:** honor configured compiler worker limits ([openai#2635](openai#2635)) ([aa08b44](openai@aa08b44)) * **build:** narrow Git-install staging detection ([openai#2621](openai#2621)) ([a6170d3](openai@a6170d3)) * **build:** postprocess the selected package manifest ([openai#2620](openai#2620)) ([1850d58](openai@1850d58)) * **build:** preserve CommonJS source-map positions ([openai#2623](openai#2623)) ([df0034d](openai@df0034d)) * **build:** preserve config-relative compiler project paths ([openai#2637](openai#2637)) ([3acb064](openai@3acb064)) * **build:** preserve declaration file import specifiers ([openai#2659](openai#2659)) ([0098c2e](openai@0098c2e)) * **ci:** propagate Node matrix check failures ([openai#2569](openai#2569)) ([bff92c9](openai@bff92c9)) * **ci:** serialize release-please branch updates ([openai#2631](openai#2631)) ([bb0e400](openai@bb0e400)) * clean up failed mock-server startups ([openai#2558](openai#2558)) ([83c92f0](openai@83c92f0)) * **deps:** bump qs to 6.16.0 for security fixes ([openai#2588](openai#2588)) ([4e5c6e3](openai@4e5c6e3)) * **docs:** await headers in Next.js webhook examples ([openai#2595](openai#2595)) ([7e48f05](openai@7e48f05)) * **docs:** correct the chat streaming helper contract ([openai#2596](openai#2596)) ([135ac7e](openai@135ac7e)) * **docs:** include image detail in the Vision example ([openai#2598](openai#2598)) ([e76bb16](openai@e76bb16)) * **docs:** omit absent Bedrock provider session tokens ([openai#2597](openai#2597)) ([41f85bd](openai@41f85bd)) * **ecosystem:** preserve retry delays in parallel workers ([openai#2673](openai#2673)) ([6164745](openai@6164745)) * **ecosystem:** reject invalid retry counts ([openai#2642](openai#2642)) ([d0e2bcf](openai@d0e2bcf)) * **ecosystem:** restore explicit web type-test module markers ([openai#2584](openai#2584)) ([3c189f4](openai@3c189f4)) * **embeddings:** preserve numeric output with body overrides ([openai#2549](openai#2549)) ([97d1ae7](openai@97d1ae7)) * **embeddings:** preserve numeric responses after request hooks ([openai#2590](openai#2590)) ([5202b13](openai@5202b13)) * **embeddings:** reject incomplete float32 vectors ([openai#2615](openai#2615)) ([3940a20](openai@3940a20)) * **embeddings:** validate response collection before decoding ([openai#2686](openai#2686)) ([888811f](openai@888811f)) * **events:** preserve unbound listener receivers ([openai#2644](openai#2644)) ([c8238e7](openai@c8238e7)) * **examples:** annotate Responses WebSocket turn result ([openai#2585](openai#2585)) ([dde19c5](openai@dde19c5)) * **examples:** avoid extra requests for empty tool calls ([openai#2672](openai#2672)) ([be292d3](openai@be292d3)) * **examples:** avoid unhandled WebSocket rejections ([openai#2669](openai#2669)) ([744b0ad](openai@744b0ad)) * **examples:** cancel Edge streams when requests abort ([openai#2530](openai#2530)) ([18100a1](openai@18100a1)) * **examples:** close failed streaming responses ([openai#2531](openai#2531)) ([eb9b468](openai@eb9b468)) * **examples:** close multi-agent WebSockets on terminal responses ([openai#2533](openai#2533)) ([51ec453](openai@51ec453)) * **examples:** drain manual conversation error output ([openai#2629](openai#2629)) ([e488b53](openai@e488b53)) * **examples:** find structured-output tool calls ([openai#2572](openai#2572)) ([84870a8](openai@84870a8)) * **examples:** keep tracking fine-tuning file validation ([openai#2528](openai#2528)) ([63312f2](openai@63312f2)) * **examples:** preserve background stream resume cursors ([openai#2671](openai#2671)) ([b7398e3](openai@b7398e3)) * **examples:** reject early multi-agent WebSocket closure ([openai#2599](openai#2599)) ([c6320f0](openai@c6320f0)) * **examples:** reject early Realtime socket closure ([openai#2639](openai#2639)) ([7db48ec](openai@7db48ec)) * **examples:** reject incomplete image streams ([openai#2617](openai#2617)) ([ef39397](openai@ef39397)) * **examples:** reject incomplete multi-agent streams ([openai#2666](openai#2666)) ([152b98d](openai@152b98d)) * **examples:** reject unsuccessful background responses ([openai#2600](openai#2600)) ([902a931](openai@902a931)) * **examples:** reject unsuccessful Responses streams ([openai#2645](openai#2645)) ([86acb95](openai@86acb95)) * **examples:** reject unsuccessful structured-tool responses ([openai#2668](openai#2668)) ([99aa303](openai@99aa303)) * **examples:** report audio demo failures ([openai#2570](openai#2570)) ([29c0572](openai@29c0572)) * **examples:** report image-streaming failures ([openai#2563](openai#2563)) ([947c555](openai@947c555)) * **examples:** report multi-agent streaming failures ([openai#2550](openai#2550)) ([19d9151](openai@19d9151)) * **examples:** report nonstreaming request failures ([openai#2603](openai#2603)) ([384dc26](openai@384dc26)) * **examples:** report premature WebSocket closure ([openai#2544](openai#2544)) ([7eb5f7e](openai@7eb5f7e)) * **examples:** report unsuccessful Azure Realtime responses ([openai#2612](openai#2612)) ([2b62209](openai@2b62209)) * **examples:** report unsuccessful fine-tuning jobs ([openai#2616](openai#2616)) ([e761c16](openai@e761c16)) * **examples:** report unsuccessful Realtime responses ([openai#2559](openai#2559)) ([5d3e584](openai@5d3e584)) * **examples:** respect raw streaming proxy backpressure ([openai#2663](openai#2663)) ([a11bb1b](openai@a11bb1b)) * **examples:** reuse completed background responses ([openai#2557](openai#2557)) ([8e1977b](openai@8e1977b)) * **examples:** serialize missing book lookup results ([openai#2573](openai#2573)) ([db7e371](openai@db7e371)) * **examples:** stop polling failed training files ([openai#2592](openai#2592)) ([1d67945](openai@1d67945)) * **examples:** support redirected streaming output ([openai#2546](openai#2546)) ([8df2e3a](openai@8df2e3a)) * **examples:** use the default audio recording input ([openai#2593](openai#2593)) ([6153484](openai@6153484)) * **format:** ignore missing literal file-list paths ([openai#2670](openai#2670)) ([516a849](openai@516a849)) * **format:** preserve file-list scope ([openai#2568](openai#2568)) ([7faff0e](openai@7faff0e)) * identify modern Edge browser runtime headers ([openai#2628](openai#2628)) ([9cdf6f0](openai@9cdf6f0)) * isolate monthly node review publication ([openai#2691](openai#2691)) ([3970dae](openai@3970dae)) * isolate realtime connection credentials ([openai#2690](openai#2690)) ([e9db491](openai@e9db491)) * keep formatter inputs on LF across Git checkouts ([openai#2515](openai#2515)) ([074618f](openai@074618f)) * **lint:** preserve leading-dash generated file paths ([openai#2640](openai#2640)) ([3b91a83](openai@3b91a83)) * **pagination:** follow next cursors through empty pages ([openai#2561](openai#2561)) ([9ea39ce](openai@9ea39ce)) * parse JSON media types case-insensitively ([openai#2551](openai#2551)) ([7ea7926](openai@7ea7926)) * **polling:** honor zero custom polling intervals ([openai#2518](openai#2518)) ([0cd94bb](openai@0cd94bb)) * **polling:** treat a cancelled vector store file as terminal ([openai#2506](openai#2506)) ([43c1007](openai@43c1007)) * preserve alternate Headers inputs ([openai#2560](openai#2560)) ([5d36ff4](openai@5d36ff4)) * preserve CommonJS constructor inheritance ([openai#2547](openai#2547)) ([aafb64e](openai@aafb64e)) * preserve cross-realm error causes ([openai#2555](openai#2555)) ([bfec504](openai@bfec504)) * preserve own Zod schema definition names ([openai#2695](openai#2695)) ([f65d190](openai@f65d190)) * preserve package selection in parallel ecosystem tests ([openai#2516](openai#2516)) ([fa0ce41](openai@fa0ce41)) * preserve streamed API error classification ([openai#2694](openai#2694)) ([8e2c6ee](openai@8e2c6ee)) * **realtime:** preserve base URL routing components ([openai#2567](openai#2567)) ([0f2e22d](openai@0f2e22d)) * **responses:** omit parsed values from replayed history ([openai#2634](openai#2634)) ([124b652](openai@124b652)) * **responses:** parse namespaced function tools ([openai#2574](openai#2574)) ([4d86684](openai@4d86684)) * **responses:** validate indexed response collections ([openai#2687](openai#2687)) ([1ecbf52](openai@1ecbf52)) * **runners:** observe cancellation after completion callbacks ([openai#2591](openai#2591)) ([27f5c6a](openai@27f5c6a)) * **runners:** preserve frozen input history ([openai#2525](openai#2525)) ([1211038](openai@1211038)) * **runners:** reuse caller abort listeners across tool turns ([openai#2594](openai#2594)) ([57d0da4](openai@57d0da4)) * **runners:** suppress content events for input history ([openai#2520](openai#2520)) ([37d6fc2](openai@37d6fc2)) * serialize tool-call IDs once per completion ([openai#2688](openai#2688)) ([8afb23e](openai@8afb23e)) * **streaming:** abort before early-exit cleanup ([openai#2613](openai#2613)) ([03acf94](openai@03acf94)) * **streaming:** avoid duplicate terminal tool argument events ([openai#2658](openai#2658)) ([12e5db6](openai@12e5db6)) * **streaming:** interrupt pending reads on cancellation ([openai#2556](openai#2556)) ([2d7a767](openai@2d7a767)) * **streaming:** surface server error frames and settle streams past throwing listeners ([openai#2502](openai#2502)) ([17a661e](openai@17a661e)) * support Edge Runtime without Node process ([openai#2552](openai#2552)) ([5dc4deb](openai@5dc4deb)) * **test:** cancel Steady commands waiting for cache locks ([openai#2636](openai#2636)) ([8896395](openai@8896395)) * **test:** preserve unrelated processes on mock startup failure ([openai#2622](openai#2622)) ([6896e66](openai@6896e66)) * **test:** reap Steady commands when lease creation fails ([openai#2664](openai#2664)) ([518756a](openai@518756a)) * **test:** reject HTTP errors in mock health checks ([openai#2667](openai#2667)) ([7b9e0b0](openai@7b9e0b0)) * **test:** reject unknown ecosystem project names ([openai#2626](openai#2626)) ([fcd12bb](openai@fcd12bb)) * **test:** run Bun validation with --from-npm ([openai#2625](openai#2625)) ([74f58a9](openai@74f58a9)) * **test:** use a monotonic Steady lock timeout ([openai#2661](openai#2661)) ([7b5d120](openai@7b5d120)) * **types:** account for optional event arguments ([openai#2527](openai#2527)) ([548a651](openai@548a651)) * **types:** include missing parsed response stream events ([openai#2521](openai#2521)) ([6a9db07](openai@6a9db07)) * **types:** preserve custom tool factory metadata ([openai#2526](openai#2526)) ([13dca7b](openai@13dca7b)) * **types:** preserve runnable function tuple positions ([openai#2524](openai#2524)) ([e345d0f](openai@e345d0f)) * **uploads:** accept cross-realm ArrayBuffers ([openai#2529](openai#2529)) ([69d7bda](openai@69d7bda)) * **uploads:** preserve explicit empty filenames ([openai#2532](openai#2532)) ([eabb487](openai@eabb487)) * **uploads:** reject zero batch upload concurrency ([openai#2519](openai#2519)) ([8b9405a](openai@8b9405a)) * validate partial image indices in streaming example ([openai#2693](openai#2693)) ([a9a4440](openai@a9a4440)) * **zod:** preserve strict schema compatibility with Zod 4.5.4 ([openai#2677](openai#2677)) ([c11870e](openai@c11870e)) * **zod:** retain the original tool parameter schema ([openai#2662](openai#2662)) ([fdb038e](openai@fdb038e)) ### Performance Improvements * **bedrock:** avoid copying repeated query values ([openai#2545](openai#2545)) ([80ab919](openai@80ab919)) ### Chores * **api:** document throttling and model overload responses — SDK-235 ([openai#2632](openai#2632)) ([9dd0524](openai@9dd0524)) * clarify Response types and remove unused handwritten code ([openai#2660](openai#2660)) ([86152ab](openai@86152ab)) * **deps-dev:** bump @aws-sdk/credential-provider-node from 3.972.78 to 3.972.81 ([openai#2681](openai#2681)) ([48629a3](openai@48629a3)) * **deps-dev:** bump @cloudflare/workers-types from 5.20260816.1 to 5.20260823.1 in /ecosystem-tests/cloudflare-worker ([openai#2535](openai#2535)) ([dc14bf5](openai@dc14bf5)) * **deps-dev:** bump @cloudflare/workers-types from 5.20260823.1 to 5.20260830.1 in /ecosystem-tests/cloudflare-worker ([openai#2676](openai#2676)) ([ae04066](openai@ae04066)) * **deps-dev:** bump @smithy/signature-v4 from 5.7.2 to 5.7.3 ([openai#2679](openai#2679)) ([41ca20d](openai@41ca20d)) * **deps-dev:** bump @swc/core from 1.15.47 to 1.16.1 ([openai#2538](openai#2538)) ([5c04dc4](openai@5c04dc4)) * **deps-dev:** bump fast-uri from 3.1.5 to 3.1.7 in /ecosystem-tests/ts-browser-webpack ([openai#2579](openai#2579)) ([34380bf](openai@34380bf)) * **deps-dev:** bump fast-uri from 3.1.5 to 3.1.7 in /ecosystem-tests/vercel-edge ([openai#2577](openai#2577)) ([511d1e4](openai@511d1e4)) * **deps-dev:** bump oxlint from 1.76.0 to 1.79.0 ([openai#2542](openai#2542)) ([2683c3a](openai@2683c3a)) * **deps-dev:** bump oxlint from 1.79.0 to 1.80.0 ([openai#2683](openai#2683)) ([02d5fb0](openai@02d5fb0)) * **deps-dev:** bump puppeteer from 25.7.0 to 25.8.0 in /ecosystem-tests/browser-direct-import ([openai#2534](openai#2534)) ([f039c09](openai@f039c09)) * **deps-dev:** bump puppeteer from 25.7.0 to 25.8.0 in /ecosystem-tests/ts-browser-webpack ([openai#2539](openai#2539)) ([5828818](openai@5828818)) * **deps-dev:** bump puppeteer from 25.8.0 to 25.9.0 in /ecosystem-tests/browser-direct-import ([openai#2675](openai#2675)) ([bebd72f](openai@bebd72f)) * **deps-dev:** bump puppeteer from 25.8.0 to 25.9.0 in /ecosystem-tests/ts-browser-webpack ([openai#2680](openai#2680)) ([fe3770f](openai@fe3770f)) * **deps-dev:** bump ultracite from 7.10.0 to 7.10.5 ([openai#2537](openai#2537)) ([6d37b30](openai@6d37b30)) * **deps-dev:** bump vitest from 4.1.10 to 4.1.11 ([openai#2541](openai#2541)) ([6ea24b3](openai@6ea24b3)) * **deps-dev:** bump webpack from 5.109.2 to 5.110.2 in /ecosystem-tests/ts-browser-webpack ([openai#2682](openai#2682)) ([73405d1](openai@73405d1)) * **deps-dev:** bump wrangler from 4.125.0 to 4.127.1 in /ecosystem-tests/cloudflare-worker ([openai#2678](openai#2678)) ([0d92742](openai@0d92742)) * **deps:** bump @azure/identity from 4.13.1 to 4.13.2 ([openai#2540](openai#2540)) ([0318287](openai@0318287)) * **deps:** bump express and @types/express ([openai#2684](openai#2684)) ([c581c17](openai@c581c17)) * **ecosystem:** remove unused proxy stub ([openai#2641](openai#2641)) ([ce1713a](openai@ce1713a)) * migrate to forked steady ([openai#2587](openai#2587)) ([67ce45d](openai@67ce45d)) ### Documentation * correct Bun ecosystem test instructions ([openai#2627](openai#2627)) ([3434b42](openai@3434b42)) * correct error handling diagnostics ([openai#2630](openai#2630)) ([d334d8c](openai@d334d8c)) * **helpers:** remove undefined main call from parsing example ([openai#2648](openai#2648)) ([d8f6be9](openai@d8f6be9)) * **realtime:** report unsuccessful responses in the guide ([openai#2647](openai#2647)) ([f58b1fd](openai@f58b1fd)) * **streaming:** reject unsuccessful background responses ([openai#2614](openai#2614)) ([1025f7a](openai@1025f7a)) * **tools:** support TypeScript 4.9 in the function loop ([openai#2649](openai#2649)) ([42e852b](openai@42e852b)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: openai-sdks[bot] <284451331+openai-sdks[bot]@users.noreply.github.com>
Summary
Require a coordinator
response.completedevent before the multi-agent SSE example treats stream termination as success. The production change is six added lines in the handwritten example.Child-agent completion/failure remains nonterminal for the coordinator. The example still consumes the stream after coordinator completion, preserving explicit root failures, named SSE errors, and transport errors rather than hiding them with an early break.
Reproduction
On current main, the actual executable example exits successfully when the server sends
[DONE]or closes the SSE body before the coordinator completes—even with only partial coordinator text or a completed/failed child agent. Ten new executable regressions fail before the source change, while fourteen controls pass.Validation
[DONE], omitted/null/explicit root ownership, child-status continuation, and a named SSE error received after coordinator completion.git diff --checkpass. The entire built SDK is byte-for-byte identical to the base build.Only the example and its existing regression-test file change. No SDK runtime/parser changes, generated files, dependencies, new payload limits, or WebSocket changes. All inputs and responses are synthetic; no live API calls. This complements the existing explicit-terminal-error handling and the separate WebSocket early-close fix.