Skip to content

fix(agent): prevent unhandled rejection when aborting stream - #122

Open
warun7 wants to merge 2 commits into
OpenRouterTeam:mainfrom
warun7:fix/unhandled-rejection-on-stream-abort
Open

warun7 wants to merge 2 commits into
OpenRouterTeam:mainfrom
warun7:fix/unhandled-rejection-on-stream-abort

Conversation

@warun7

@warun7 warun7 commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #113

Summary of Changes

  • Attached a no-op rejection handler to executionPromise inside startTurnBroadcasterExecution() in packages/agent/src/lib/model-result.ts. This ensures that when a stream consumer aborts or terminates iteration early, the background promise rejection does not trigger an unhandledRejection event.
  • Added regression unit test in packages/agent/tests/unit/run-cancellation.test.ts.
  • Added changeset file for @openrouter/agent (patch).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
LukasParke added a commit that referenced this pull request Oct 21, 2026
Port of OpenRouterTeam/typescript-sdk#773, re-implemented for the agent
engine.

- New client-only `streamTimeout` option ({ firstContentMs,
  contentIntervalMs }) on CallModelInput / GetResponseOptions. It is
  stripped from the API request on both resolution paths.
- All five betaResponsesSend call sites (initial, tool-round follow-up,
  final, empty-output retry, unsent-results resume) now go through one
  sendModelRequest() helper, so deadlines re-arm for every request.
- With a deadline configured, each request gets its own AbortController.
  dispatchRequestOptions composes it with the run signal, the caller
  signal and the replicated timeoutMs. On a stall the watchdog aborts
  that controller, which tears down the hung request. An abort of the
  composed signal (user or timeoutMs) disarms the watchdog, so it is
  reported as itself.
- Same no-op rejection handler on executionPromise as PR #122: an early
  consumer exit followed by a stalled follow-up turn must not produce an
  unhandled rejection.

Option named `streamTimeout` rather than the SDK's `timeout`:
CallModelInput spreads every ResponsesRequest key, and a bare `timeout`
would collide with any future API field of that name.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

Aborting a tool-enabled stream leaves executionPromise unhandled and can terminate Node.js

2 participants