fix(ai-chat): terminate WebSocket chat transport stream on abort#986
Merged
threepointone merged 1 commit intomainfrom Feb 25, 2026
Merged
fix(ai-chat): terminate WebSocket chat transport stream on abort#986threepointone merged 1 commit intomainfrom
threepointone merged 1 commit intomainfrom
Conversation
Close the ReadableStream returned by WebSocketChatTransport.sendMessages() when the caller's AbortSignal fires or stream.cancel() is called. Without this, useChat remains stuck in the 'streaming' state after stop/cancel. - Consolidate done/error/abort cleanup into a single finish() helper - Send CF_AGENT_CHAT_REQUEST_CANCEL on abort and stream.cancel() - Add tests for mid-stream abort, pre-aborted signal, stream.cancel(), and normal completion through the transport Fixes #985
🦋 Changeset detectedLatest commit: a3c2897 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #985
The
ReadableStreamreturned byWebSocketChatTransport.sendMessages()was never terminated when the caller'sAbortSignalfired orstream.cancel()was called. This leftuseChatstuck in the "streaming" state after the user clicked Stop/Cancel.What changed
packages/ai-chat/src/ws-chat-transport.tsfinish(action)helper that gates on acompletedflag, cleans upactiveRequestIds, and detaches the WS listener viaabortController.abort().onAbort()sendsCF_AGENT_CHAT_REQUEST_CANCELto the server (best-effort), then errors the stream with anAbortErrorviafinish().stream.cancel()delegates toonAbort(), so cancelling the stream from the consumer side also notifies the server.abortSignallistener is registered with{ once: true }and handles already-aborted signals.packages/ai-chat/src/tests/ws-transport-abort.test.ts(new)Four test cases exercising the transport directly:
stream.cancel()— verifies cancel completes without hangingHow this differs from the proposed fix in #985
The community fix is correct and solves the bug, but our implementation simplifies the approach:
abortRequested+completed)completed)requestAbort+completeAbort)finish)start()race guardabortRequestedinstart()to handle "abort before stream starts"start()runs synchronously duringReadableStreamconstruction, sostreamControlleris always set before any listener can firedone/errorpaths inonMessagehave separate inline cleanupfinish()Notes for reviewers
let streamController!: ReadableStreamDefaultControlleruses!which is new to this codebase. The comment above it explains why it is safe (synchronousstart()). If you prefer| undefinedwith optional chaining, happy to change.sendMessages()is called,onAbort()fires immediately but the request is still sent on the next line. This is harmless — the server receives the cancel and stops, and the client stream is already errored. Adding a guard would be possible but adds complexity for a negligible edge case.useAgentChathook'sonAgentMessagehandler (CF_AGENT_STREAM_RESUMINGprotocol), which is a separate path from the transport'sReadableStream.npx vitest --project workers --run).