fix: prevent expected Copilot auth error from leaking as unhandled rejection (fixes #321950)#321956
Open
vs-code-engineering[bot] wants to merge 1 commit into
Open
Conversation
…jection (fixes #321950) trackSessionCreate observed the createSession promise via `void promise.finally(...)`. Promise.finally re-raises the settled rejection, so when createSession rejected with a by-design error (e.g. AHP_AUTH_REQUIRED when unauthenticated) the cleanup chain surfaced a duplicate unhandled rejection on the renderer, even though the real caller and the server-side logService.error already handle the error. Add a trailing no-op catch on the observation branch so the inflight-map cleanup never participates in unhandled-rejection telemetry. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
When
createSessionis invoked while the user is not authenticated, the agent host rejects with a by-designProtocolError(AHP_AUTH_REQUIRED)("Authentication is required to use Copilot"). That rejection is already handled by the awaiting caller and logged server-side, butAgentSubscriptionManager.trackSessionCreatealso observes the same promise viavoid promise.finally(...).Promise.prototype.finallyre-raises the settled rejection, and because the cleanup chain has nocatch, the expected error is reported a second time as a renderer unhandled rejection — which is what telemetry buckets asunhandlederror-Authentication is required to use Copilot. The.codethat would normally letBaseErrorTelemetryfilter aProtocolErroris stripped during IPC serialization, so the re-wrapped error slips through the filter.Fixes #321950
Recommended reviewer:
@dmitrivMSCulprit Commit
5024d718@dmitrivMS_inflightCreates/trackSessionCreatemachinery (to make a subscribe wait for an in-flight create). The newvoid promise.finally(...)cleanup branch observes the create promise without acatch, so a rejected create now leaks an unhandled rejection. The bucket first appeared 2026-06-17, one day after this commit landed (2026-06-16), and is confined to 1.126.0-insider.Code Flow
sequenceDiagram participant Caller as Renderer caller participant Local as localAgentHostService.createSession participant Track as AgentSubscriptionManager.trackSessionCreate participant Host as agent host (copilotAgent._ensureClient) participant Tel as Renderer ErrorTelemetry Caller->>Local: createSession(config) Local->>Host: _proxy.createSession(config) [RPC] Note over Host: Crash site (by-design):<br/>throw ProtocolError(AHP_AUTH_REQUIRED)<br/>when unauthenticated — logged via logService.error Host-->>Local: rejected promise (.code stripped by IPC serialization) Local->>Track: trackSessionCreate(uri, promise) Note over Track: ⚠️ Root cause:<br/>void promise.finally(cleanup) re-raises<br/>the rejection with no catch handler Track-->>Tel: unhandled rejection Note over Tel: reported as "UnhandledError -<br/>Authentication is required to use Copilot" Local-->>Caller: rejected promise (properly handled/awaited by caller)Affected Files
src/vs/platform/agentHost/common/state/agentSubscription.ts— fixed.trackSessionCreate(the producer of the spurious unhandled rejection).src/vs/platform/agentHost/electron-browser/localAgentHostService.ts— context only (not changed).createSessionregisters the same promise withtrackSessionCreateand also returns it to the caller; the dual reference is what lets the observation branch leak while the caller still handles the error.src/vs/platform/agentHost/node/copilot/copilotAgent.ts— context only (not changed)._ensureClientthrowsProtocolError(AHP_AUTH_REQUIRED); this is the legitimate, by-design crash site and is intentionally left untouched.Repro Steps
localAgentHostService.createSession(config)for a pre-specified session URI (e.g. restoring/opening a chat session before sign-in completes)._ensureClientthrowsProtocolError(AHP_AUTH_REQUIRED); the create promise rejects.unhandlederror-Authentication is required to use Copilot, even though the awaiting caller handled the rejection and the server logged it.How the Fix Works
Chosen approach (
src/vs/platform/agentHost/common/state/agentSubscription.ts, bypass siteagentSubscription.ts:790): append a trailing no-op.catch(() => { })to thevoid promise.finally(...)cleanup chain intrackSessionCreate.Promise.prototype.finallypasses the original rejection through, so the cleanup chain — whose only job is to evict the_inflightCreatesmap entry — re-raised the rejection as an unhandled rejection on a pure observation branch.This is the data-producer fix, not a crash-site fix: the change is applied exactly where the spurious unhandled rejection is produced. The real
createSessionrejection remains owned and handled by the awaiting caller and is still logged server-side vialogService.error, so the no-op catch suppresses only the duplicate observation, not the real error — no error is hidden from logs or telemetry. This mirrors an existing deliberate swallow in the same class (getSubscription, lines 830–834), which already discards this same promise's rejection because the failure surfaces consistently viasetError().Alternatives considered:
try/catchat the crash site incopilotAgent._ensureClient— rejected:AHP_AUTH_REQUIREDis a by-design, client-handleable protocol error (code -32007); the throw and itslogService.errorare correct and must stay, and suppressing there would hide a legitimate error from the telemetry pipeline.createSessioncall/return intry/catch— rejected: that swallows an error the caller legitimately needs, hiding the symptom instead of fixing the producer of the unhandled rejection..then(cleanup, cleanup)— equivalent runtime behavior, but it duplicates the cleanup callback and changes more lines; the trailingcatchis the more minimal, intent-preserving edit.Recommended Owner
@dmitrivMS— author of the culprit commit/PR #321526, which introducedtrackSessionCreate.5024d718on 2026-06-16, two days before the checked-out HEAD of 2026-06-18).src/vs/platform/agentHost/**path (not a vendoredextensions/**path) via GitHub's merge flow, indicating write access. The agent'sghis unauthenticated and no single-user-permission MCP tool is available, so the collaborators endpoint could not be queried directly; membership is inferred from the merged core-platform PR.The issue is currently triage-assigned to
bryanchen-d(error-fix driver);@dmitrivMSis the code owner for the introduced logic.