{{ message }}
Repository navigation
fix: guard Auto routing stream pushes against cancelled turns (fixes #341067) - #341077
Merged
Logan Ramos (lramos15) merged 1 commit intoOct 13, 2026
Conversation
…icrosoft#341067) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The cancellation guard addresses the reported lifecycle race and all callers and tests are updated consistently.
0 open findings
What changed in this PR
Guards Auto-routing response updates after chat-turn cancellation, preventing writes to a closed response stream.
Changes:
- Passes the request cancellation token into Auto-routing reporting.
- Skips routing status pushes after cancellation.
- Adds regression coverage for cancelled turns.
🧠 Review effort: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Logan Ramos (lramos15)
approved these changes
Oct 12, 2026
Vritant Bhardwaj (vritant24)
approved these changes
Oct 12, 2026
Bhavya U (bhavyaus)
approved these changes
Oct 12, 2026
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
reportAutoModeRoutinginstalls anonDidRoutelistener that pushes Auto explainability parts (ChatResponseAutoModeResolutionPart) into the turn'sChatResponseStreamwhen routing starts and resolves. Routing resolves asynchronously (POST /auto), so when the user cancels the turn mid-route, VS Code core closes the response stream before the listener fires. The subsequentstream.push(...)throwsError: Response stream has been closed, which escapesresolveAutoModeEndpointand reaches the unhandled-error telemetry pipeline. It affected 108 users onGitHub.copilot-chat0.69.0.Fixes #341067
Recommended reviewer:
@spurwahaCulprit Commit
d7132ac6c9b5@spurwahareportAutoModeRoutingto pushChatResponseAutoModeResolutionPartonto the turn's stream from inside the asynconDidRoutelistener (lines 110 and 114).git blameattributes bothstream.push(...)lines to this commit, and the bucket first appears in 0.69.0 (the first release shipping it) with a clean 0.68.0 baseline. Before it, routing state was not written back to the stream, so a cancelled turn could not push to a closed stream.Code Flow
sequenceDiagram participant User as User participant Handler as chatParticipants handler participant Service as AutomodeService participant Listener as onDidRoute listener participant Stream as ChatResponseStream User->>Handler: start Auto turn Handler->>Service: resolveAutoModeEndpoint(request) Note over Service: await POST /auto<br/>(async routing) User->>Stream: cancel turn Note over Stream: ⚠️ Root cause:<br/>core closes the stream on cancel Service->>Listener: fire onDidRoute (started / resolved) Listener->>Stream: stream.push(ChatResponseAutoModeResolutionPart) Note over Stream: 💥 Error thrown:<br/>Response stream has been closedAffected Files
extensions/copilot/src/platform/endpoint/node/automodeService.tsstream.push(new ChatResponseAutoModeResolutionPart(...))inside the asynconDidRoutelistener — pushes after the stream was closed by cancellationextensions/copilot/src/platform/endpoint/node/automodeService.tsresolveAutoModeEndpointfires_onDidRouteafter an awaited_resolveAutoModeEndpoint, so the listener runs post-cancellationextensions/copilot/src/extension/conversation/vscode-node/chatParticipants.tsreportAutoModeRouting; owns the requesttokenthat signals cancellationRepro Steps
POST /auto(no cached endpoint).onDidRoutelistener callsstream.push(...)on the closed stream andError: Response stream has been closedis thrown. Because routing latency is network-bound, the race is easiest to hit on slow connections or by cancelling immediately after submitting.How the Fix Works
Chosen approach —
automodeService.ts→reportAutoModeRouting(): thread the turn'sCancellationTokeninto the function and guard bothstream.push(...)calls with!token.isCancellationRequested. The chat participant handler (chatParticipants.ts:211) already receives the requesttokenand now forwards it. Once the turn is cancelled, core has closed the stream, so the explainability part is both unsafe to push and moot (there is no live turn to annotate) — skipping the push is the correct behavior, not merely error avoidance.This is a use-after-close lifecycle race where the producer that closes the stream is VS Code core's chat request plumbing, outside the extension's reach — the extension cannot reorder or block core's stream teardown. The guard therefore lives at the consumer (the listener), which is the justified place for an external/untrusted-boundary lifecycle fix. The fix sits at the real bypass (the unconditional push onto a possibly-closed stream), not at a generic catch-all.
Why this fix: after this change, the listener at
automodeService.ts:110and:114cannot callstream.push(...)once the turn's token is cancelled, which is exactly when core has closed the stream — so theResponse stream has been closedthrow on that path is no longer reachable.Alternatives considered:
stream.push(...)calls intry/catch— rejected: swallows the error, hides any future unrelated stream failures, and does not express the real invariant (don't push to a cancelled turn).ChatResponseStreamImplsilently drop pushes after close — rejected: changes shared stream semantics for every caller to paper over one producer's post-cancel push.Lifecycle pattern: use-after-dispose (stream closed by cancellation, listener fires afterward).
Producer site: VS Code core chat request plumbing closes the
ChatResponseStreamon cancellation — not reachable fromextensions/copilot. Consumer-side guard justification: the only in-repo signal the extension has for this teardown is the requestCancellationToken, so gating the push on that token is the closest-to-producer fix available to the extension.Recommended Owner
@spurwaha— author of the culprit commitd7132ac6c9b5(PR #336757), which introduced the stream pushes inreportAutoModeRouting. Active inmicrosoft/vscodewithin the last 90 days and lands changes in coresrc/vs(write access).