Repository navigation
fix: preserve chat runner abort reasons - #2607
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. |
sylvesterkaczmarek
left a comment
There was a problem hiding this comment.
Reason propagation is now consistent across both cancellation boundaries: external signals transfer their original reason into the runner controller, and tool checkpoints wrap that same reason in APIUserAbortError.cause. Covering already-aborted signals is important because that path bypasses listener delivery entirely.
…#2591) ## Summary Cancelling a tool runner during its final `afterCompletion` callback could be reported as success: `done()` resolved and successful final events were emitted even though the runner controller was aborted. This PR observes callback cancellation at the existing post-callback boundary and again at successful final settlement: - Continue awaiting the configured callback at all three exit sites: a final response, a forced function call, and the completion limit. - Retain the early cancellation check before another request, and use one final-settlement check to cover later fulfillment reactions on a callback's returned promise. - Preserve the runner signal's reason by identity on `APIUserAbortError.cause`. - Preserve completed tool results and the original error when the callback itself rejects. The final-settlement check is gated by a private flag set only when `afterCompletion` is actually invoked. Omitted/null callbacks, configured-but-never-invoked callbacks, and unrelated `ChatCompletionStream` behavior remain unchanged. ## Scope Only handwritten `src/lib/AbstractChatCompletionRunner.ts`, its existing cancellation test, and `docs/helpers.md` change. No generated files, dependencies, exported types, or general `EventStream` machinery change. [openai#2607](openai#2607) owns the broader external-signal reason forwarding and five preexisting tool checkpoints. Those changes are not duplicated here. This PR preserves the reason already present on the runner's signal at the new callback boundaries and composes with that separate forwarding fix. ## Verification - Fail-before regressions cover callback/external cancellation, cause identity, and the late-promise-reaction race across JSON/SSE and every exit route. The final settlement addition made eight previously failing late-reaction cases pass while preserving 34 controls. - **118 related tests pass** on Node 22.22.2 and 24.19.0, including callback-error-precedence and no-callback controls. - **474 final built CJS/ESM checks pass** across exact Node 22.0.0, 24.19.0, and 26.7.0: a 324-case runner matrix plus 150 independent compatibility checks. These verify cause identity/descriptors, arbitrary/falsy reasons, late reactions, one request, retained history, lifecycle counts, omitted/null callbacks, never-invoked callbacks, and bare streams. - `tsc --noEmit`, canonical `./scripts/build`, pinned Oxfmt 0.62.0, available cached Oxlint 1.76.0, and `git diff --check` pass. Emitted CJS/ESM runner declarations remain byte-identical. - Full handwritten unit suite on the final revision: **7,450 passed, 1 failed**. The sole `oxlint-config` package-command dependency-verification failure reproduces identically on the clean `dde19c5c` base. - Repeated independent adversarial review covered security, compatibility, callback/error precedence, final-settlement ordering, and regression gaps. No blocking implementation findings remain within this callback scope. ## Review follow-ups - [`718da1c6`](openai@718da1c) adds cause preservation at the new checkpoint. - [`4ae4ac34`](openai@4ae4ac3) closes the late-promise-reaction gap with the callback-scoped settlement guard. ## Local tooling limitation Fresh frozen installation remains blocked by missing publication-time metadata for `qs@6.16.0` in the configured registry. Local checks use available cached Vitest 4.1.10 and Oxlint 1.76.0 rather than pinned 4.1.11 and 1.79.0; TypeScript 6.0.3 and Oxfmt 0.62.0 match their pins. Fresh CI validates the new head with the pinned toolchain. No lockfile, registry configuration, or supply-chain safeguards were changed.
HAYDEN-OAI
left a comment
There was a problem hiding this comment.
Requesting changes for a cancellation classification regression in sequential tool callbacks. Forwarding the caller's reason also requires normalizing callback rejections that match that reason into APIUserAbortError, preserving cause.
be65087 to
69e2710
Compare
|
Addressed the requested cancellation-classification case. Sequential tool callbacks now normalize a rejection that is exactly the aborted runner signal reason into APIUserAbortError while preserving that reason as cause. Added a focused parallel_tool_calls:false regression where the callback observes the caller abort via throwIfAborted(); it now emits the abort path and the focused test passes. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 69e2710cce
ℹ️ 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".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Addressed the new cancellation findings. Abort propagation now tolerates structural signals whose |
Preserve the author’s abort-reason fixes, reuse one abort-error constructor, and validate parsed and named callbacks, all streaming helpers, and structural-signal cleanup. Fix current formatting and lint issues.
HAYDEN-OAI
left a comment
There was a problem hiding this comment.
Approved at 4d333bd. All four review threads are resolved, the branch includes current main without conflicts, and independent review found no remaining actionable correctness issue.
Validation: 675 relevant tests pass on Node 22 and 24; lint, typecheck, build, and CJS/ESM pre-abort and mid-body cancellation smoke checks pass on Node 22.0.0 and 24. Coverage includes parsed/unparsed and named tool callbacks, NaN reasons, all three streaming helpers, and throwing structural-signal getters with cleanup.
GitHub CI is currently waiting for approval of the fork workflow runs; this review does not indicate those checks have passed.
Castiron custom code✅ No new custom-code files detected. 45 mixed files remain; 0 existing customizations changed. Compared 45 existing customizations unchanged
5 more in the full report. 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 35121208559 --repo openai/openai-node \
--name castiron-custom-code-35121208559-1 --dir /tmp/castiron-custom-code-35121208559-1
git apply --stat /tmp/castiron-custom-code-35121208559-1/custom-code.patch
cat /tmp/castiron-custom-code-35121208559-1/custom-code.patchOr reproduce it from an SDK checkout containing the vendored reporter: git fetch --no-tags origin 2b4c2c7f0f37aedad7adeda21bb51a1ab087ac09 4d333bd52982a82db9517717b13e02200e409c8f
python3 scripts/castiron/custom_code_report.py report \
--base 2b4c2c7f0f37aedad7adeda21bb51a1ab087ac09 \
--head 4d333bd52982a82db9517717b13e02200e409c8f --fetch --require-head-hash --public \
--out /tmp/castiron-custom-code-4d333bd52982
cat /tmp/castiron-custom-code-4d333bd52982/custom-code.patchThis is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR. |
Automated Release PR --- ## [7.17.0](v7.16.0...v7.17.0) (2026-09-16) ### Features * **api:** add compaction progress events ([#2749](#2749)) ([425502d](425502d)) ### Bug Fixes * **ci:** retry transient Deno download failures ([#2752](#2752)) ([e39037b](e39037b)) * preserve chat runner abort reasons ([#2607](#2607)) ([eda4d0c](eda4d0c)) * **realtime:** preserve native WebSocket error causes ([#2715](#2715)) ([58fa72a](58fa72a)) * **responses:** reject function-based API keys in WebSocket constructors ([#2586](#2586)) ([eea7618](eea7618)) * **runners:** reject unfinished turns in streaming runTools ([#2716](#2716)) ([937ed67](937ed67)) * **streaming:** emit terminal SSE events missing a trailing blank line ([#2726](#2726)) ([94d6418](94d6418)) * **zod:** omit impossible optional branch in strict schemas ([#2751](#2751)) ([2b4c2c7](2b4c2c7)) ### Chores * update lint tooling and tailor anti-slop for the SDK ([#2753](#2753)) ([21a03c2](21a03c2)) ### Documentation * **examples:** preserve split UTF-8 in raw stream consumer ([#2714](#2714)) ([f44152a](f44152a)) --- 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>
|
Thanks for the review and merge. I appreciate the careful pass over the abort-reason propagation and cancellation cases. |
Changes being requested
Preserve caller-provided abort reasons across chat completion convenience runners.
The direct request layer already attaches
signal.reasontoAPIUserAbortError.cause, but chat completion runners could lose or misclassify that reason at internal cancellation boundaries. This change:signal.reasonwhenEventStreambridges an external signal into its runner-owned controller;APIUserAbortError, preserving that reason ascause;Fixes #2606.
Review follow-up
The current head addresses the sequential-callback classification issue raised in review. Both parsed and unparsed tool callback paths now catch a rejection equal to
runner.controller.signal.reasonafter cancellation and translate it through the sameuserAbortError()helper. The regression exercisesparallel_tool_calls: false, cancels with a customError, has the active callback callthrowIfAborted(), and verifiesrunner.done()rejects withAPIUserAbortError, preserves the custom cause, emits the abort path, and leavesrunner.aborted === true.Fresh code/security review is running on commit
69e2710cce37947e2f51476d8b57ab3f05c836fb.Validation
Previous-head validation:
pnpm test tests/lib/ChatCompletionRunFunctions.test.ts tests/lib/runner-abort-listeners.test.ts— 48 passedpnpm exec tsc --noEmit— passedpnpm build— passedgit diff --check— passedPushed-head CI/review is required for the new regression.
Additional context & links
The change does not alter cancellation timing or add a new API; it preserves and consistently classifies the same abort-cause information already exposed by direct requests.