fix: tolerate unregistered LanguageModelIgnoredFileProvider handle in $isFileIgnored (fixes #323156)#323163
Open
vs-code-engineering[bot] wants to merge 1 commit into
Conversation
… $isFileIgnored (fixes #323156) An unknown handle in the extHost $isFileIgnored RPC handler is only reachable via a benign cross-process teardown race: the extHost disposal deletes its local provider entry synchronously while the main-thread registration is still live (the unregister RPC is async) or while an isFileIgnored call is already in flight. Return false (not ignored) for an unknown handle instead of throwing an unhandled error across the IPC boundary, matching the main-thread service's default when no provider is registered. 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
ExtHostLanguageModels.$isFileIgnoredthrowsUnknown LanguageModelIgnoredFileProviderwhen it receives an RPC for a providerhandlethat is no longer in its local_ignoredFileProvidersmap. This is a benign cross-process use-after-dispose / stale-handle race across the main-thread → extension-host RPC boundary: the extHost deletes its local provider entry synchronously on disposal while the main-thread registration is briefly still live (the$unregisterFileIgnoreProviderRPC is async), or while an$isFileIgnoredcall is already in flight the other way. The throw surfaces as an unhandled error in extHost telemetry.The issue's stable-anomaly comment reports this as a pre-existing bucket that spiked 60.5× at the
1.125.1 → 1.126.0boundary (121 users / 230 hits), with the signal "a missing guard when the provider handle is gone" — i.e. the frequency rose but the underlying defect is old.The fix adds the missing guard: return
false(not ignored) for an unknown handle instead of throwing, matching the default the main-threadLanguageModelIgnoredFilesService.fileIsIgnoredalready returns when no provider is registered.Fixes #323156
Recommended reviewer:
@roblourensCulprit Commit
The throw is pre-existing, introduced together with the feature in
bcf6a6af1c18a27b00069f7713d8bc063bcadc33— "Add LanguageModelIgnoredFileProvider (#231696)" by@roblourens(2024-10-18). Confirmed against that PR's source: the original$isFileIgnoredalready didif (!provider) { throw new Error('Unknown LanguageModelIgnoredFileProvider'); }.This is therefore a latent race, not a fresh code regression. Telemetry onset is ~mid-2026 (≈1.119 insiders), and the stable-release anomaly scan reports a 60.5× volume spike across the full
1.125.1 → 1.126.0stable range (fcf6047...7e7950d). Per that scan, onset narrowing within a stable-to-stable range is N/A for a volume spike of an existing bucket, so no single amplifying commit is pinpointed — the spike reflects more frequent provider (de)registration / implicit-context checks exercising the same old race, which the guard fixes regardless of the amplifier.Code Flow
sequenceDiagram participant Editor as Active editor change participant ICtx as ChatImplicitContext (main) participant Svc as LanguageModelIgnoredFilesService (main) participant MT as MainThreadLanguageModels (main) participant EH as ExtHostLanguageModels (extHost) participant Ext as Copilot ignore provider (extHost) Note over Ext,EH: register → _ignoredFileProviders.set(handle, provider) Editor->>ICtx: focus / selection change ICtx->>Svc: fileIsIgnored(uri) Svc->>MT: provider.isFileIgnored(uri) MT->>EH: $isFileIgnored(handle, uri) [RPC] Note over Ext,EH: provider disposed → _ignoredFileProviders.delete(handle)<br/>($unregisterFileIgnoreProvider still in flight) EH-->>EH: _ignoredFileProviders.get(handle) === undefined EH-->>MT: BEFORE: throw "Unknown LanguageModelIgnoredFileProvider" Note over EH: AFTER: return false (not ignored)Affected Files
src/vs/workbench/api/common/extHostLanguageModels.ts— crash site + fix.$isFileIgnoredlooks up_ignoredFileProviders.get(handle)and threw when absent; disposal ofregisterIgnoredFileProviderdeletes the entry synchronously.src/vs/workbench/api/browser/mainThreadLanguageModels.ts— (context, producer side)$registerFileIgnoreProviderregisters a main-thread provider whoseisFileIgnoredproxies to$isFileIgnored(handle, ...);$unregisterFileIgnoreProviderdoes the asyncdeleteAndDispose(handle).src/vs/workbench/contrib/chat/common/ignoredFiles.ts— (context, precedent)LanguageModelIgnoredFilesService.fileIsIgnoredreturnsfalsewhen there is no provider — the safe default this fix mirrors.src/vs/workbench/contrib/chat/browser/attachments/chatImplicitContext.ts— (context, trigger) callsignoredFilesService.fileIsIgnored(uri)on editor changes, the path that drives the RPC during teardown.Repro Steps
Deterministic reproduction is hard because it is a timing race, but conceptually:
vscode.lm.registerIgnoredFileProvider).ChatImplicitContextperiodically callsfileIsIgnoredon the active resource._ignoredFileProvidersentry while a$isFileIgnoredcall for that handle is in flight (or the main-thread registration is briefly still live).$isFileIgnoredfor a handle it already deleted and (before this fix) throwsUnknown LanguageModelIgnoredFileProvider, recorded as an unhandled error.How the Fix Works
Chosen approach —
src/vs/workbench/api/common/extHostLanguageModels.ts: in$isFileIgnored, returnfalse(file not ignored) when the handle is unknown, instead of throwing. This is a guard clause at the inbound RPC (IPC) boundary that returns the documented safe default. An unknown handle is only reachable through the benign teardown race, because handles are allocated in the extHost and are only ever echoed back by the main thread — there is no scenario where an unknown handle indicates a logic bug, so nothing real is being masked. The chosen value is exactly the default the main-threadLanguageModelIgnoredFilesService.fileIsIgnoredalready returns when no provider is registered, so behavior is consistent on both sides of the boundary.The fix sits at the boundary that fails rather than at a "data producer", because the real producer (the main-thread caller) lives in another process and cannot be synchronized with: the disposal that removes the local entry and the in-flight RPC are concurrent across processes. After this change,
extHostLanguageModels.ts$isFileIgnoredcannot emitUnknown LanguageModelIgnoredFileProviderfor a torn-down handle because theif (!provider)branch now resolvesfalseinstead of throwing. NologService.errorcall is removed and notry/catchis introduced — the guard is added before the throwing path, following the prefer-a-guard-clause-over-try/catch principle, and the inbound handler tolerates the stale cross-process handle rather than re-emitting an unhandled error.Alternatives considered:
$unregisterFileIgnoreProvidervs. localdelete): rejected — it cannot close the window for a$isFileIgnoredcall already dispatched across the IPC boundary; that call still arrives after the local entry is gone.Recommended Owner
@roblourens— owns the Chat participant API and LM Tools API per microsoft/vscode-engineeringtriage/working-areas.md, authored the feature that introduced this code (PR #231696 / commitbcf6a6af), and is actively committing to the repo (e.g. 2026-06-26). Team-membership and liveness gates both pass.