You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The Copilot code-referencing ("snippy") post-insertion check is fired as an unawaited, uncaught floating promise from NextEditProvider.runSnippy. When the snippy service returns a non-200 response, SnippyFetchService.fetch throws, handlePostInsertion re-throws, and — because the promise is neither awaited nor given a rejection handler — the error surfaces as an unhandled rejection that is reported to error telemetry. The specific 400 seen here (source too short (must be at least 110 tokens, but is 105)) is an expected server-side rejection: the client's local pre-check uses a 65-lexeme minimum while the service enforces its own, higher token minimum, so short accepted suggestions routinely trip it. Impact is noisy telemetry only — no user-facing functionality breaks.
Initial import of the Copilot Chat extension ("Hello Copilot")
Why
The fire-and-forget handlePostInsertion(...) call in runSnippy has existed without a rejection handler since the extension was vendored into microsoft/vscode. git blame shows the throwing call site predates all recent changes, so this is a pre-existing bug re-bucketed by telemetry (the specific 400 message text changed the bucket), not a new regression.
Pre-existing / re-bucketing: the underlying floating-promise pattern is not introduced by any recent commit in the regression window; the new bucket is driven by the server's error-message text, not by a change in triggering logic.
Code Flow
sequenceDiagram
participant User as User accepts NES
participant NEP as NextEditProvider.runSnippy
participant Svc as SnippyService.handlePostInsertion
participant Fetch as SnippyFetchService.fetch
participant Server as Snippy service
User->>NEP: handleAcceptance()
Note over NEP: ⚠️ Root cause:<br/>handlePostInsertion() called<br/>without await / .catch
NEP->>Svc: handlePostInsertion(uri, doc, edit)
Svc->>Fetch: fetchMatch(source)
Fetch->>Server: POST snippy/match
Server-->>Fetch: 400 "source too short (110 > 105)"
Note over Fetch: 💥 throw new Error(`Failed with status 400 ...`)
Fetch-->>Svc: rejects
Svc-->>NEP: rejects (floating promise)
Note over NEP: Unhandled rejection → error telemetry
L8: MinTokenLength = 65 — client pre-check floor is looser than the service's 110-token floor, so short sources pass the client gate but are rejected by the server
Repro Steps
Use the Copilot Chat extension with Next Edit Suggestions (code referencing / public-code matching enabled).
Accept a short next-edit suggestion whose surrounding source clears the client's local 65-lexeme pre-check but is below the snippy service's token minimum (~110 tokens).
runSnippy fires handlePostInsertion; the snippy /match request returns HTTP 400 (source too short).
Because the promise is unawaited and uncaught, the rejection surfaces as an unhandled error and is reported to telemetry.
Deterministic given a source between the two thresholds; otherwise triggered whenever the service rejects the request for any reason.
How the Fix Works
Chosen approach (nextEditProvider.ts): runSnippy now awaits handlePostInsertion(...) inside a try/catch and routes any failure to this._logService.error(e, ...). This attaches a rejection handler at the fire-and-forget boundary that owns the background operation, so the failure is recorded as a handled error through the existing log/telemetry pipeline instead of escaping as an unhandled rejection. This follows the principle of fixing the error at the boundary that owns the best-effort background work — snippy code-referencing is explicitly a best-effort check, so its failures should not appear as unhandled application errors. The logService.error call preserves telemetry visibility of genuine failures; nothing is silently swallowed.
Alternatives considered:
Aligning the client MinTokenLength (65) with the server's 110-token floor — rejected: the exact server threshold is not a stable contract the client can safely hardcode, other non-200 responses would still leak, and it would not fix the underlying missing-rejection-handler defect.
Swallowing the error inside snippyServiceImpl.handlePostInsertion (removing the rethrow) — rejected: that hides all fetch failures from callers/telemetry rather than handling them at the fire-and-forget site, and would violate the "never silence errors" principle by dropping them entirely.
Recommended Owner
@ulugbekna — dominant recent author of nextEditProvider.ts (author of the most recent NES/inline-edits commits touching this file, merged into microsoft/vscode), making them the natural owner for this fire-and-forget call site in the inline-edits area.
Original error: ERR_API: [2026-08-20T15:39:49.753Z] create pull request in microsoft/vscode failed (attempt 1)
Original error: Validation Failed: {"resource":"PullRequest","code":"custom","field":"fork_collab","message":"fork_collab Fork collab can't be granted by someone without permission"} - https://docs.github.com/rest/pulls/pulls#create-a-pull-request
Retryable: false
Suggestion: This error cannot be resolved by retrying. Please check the error details and fix the underlying issue.
Summary
The Copilot code-referencing ("snippy") post-insertion check is fired as an unawaited, uncaught floating promise from
NextEditProvider.runSnippy. When the snippy service returns a non-200 response,SnippyFetchService.fetchthrows,handlePostInsertionre-throws, and — because the promise is neither awaited nor given a rejection handler — the error surfaces as an unhandled rejection that is reported to error telemetry. The specific 400 seen here (source too short (must be at least 110 tokens, but is 105)) is an expected server-side rejection: the client's local pre-check uses a 65-lexeme minimum while the service enforces its own, higher token minimum, so short accepted suggestions routinely trip it. Impact is noisy telemetry only — no user-facing functionality breaks.Fixes #331807
Recommended reviewer:
@ulugbeknaCulprit Commit
333d9a4053e(vendored-import boundary)@not-determinedhandlePostInsertion(...)call inrunSnippyhas existed without a rejection handler since the extension was vendored intomicrosoft/vscode.git blameshows the throwing call site predates all recent changes, so this is a pre-existing bug re-bucketed by telemetry (the specific 400 message text changed the bucket), not a new regression.Code Flow
sequenceDiagram participant User as User accepts NES participant NEP as NextEditProvider.runSnippy participant Svc as SnippyService.handlePostInsertion participant Fetch as SnippyFetchService.fetch participant Server as Snippy service User->>NEP: handleAcceptance() Note over NEP: ⚠️ Root cause:<br/>handlePostInsertion() called<br/>without await / .catch NEP->>Svc: handlePostInsertion(uri, doc, edit) Svc->>Fetch: fetchMatch(source) Fetch->>Server: POST snippy/match Server-->>Fetch: 400 "source too short (110 > 105)" Note over Fetch: 💥 throw new Error(`Failed with status 400 ...`) Fetch-->>Svc: rejects Svc-->>NEP: rejects (floating promise) Note over NEP: Unhandled rejection → error telemetryAffected Files
extensions/copilot/src/extension/inlineEdits/node/nextEditProvider.tsthis._snippyService.handlePostInsertion(...)called with noawaitand no.catch, so a rejection escapes as an unhandled errorextensions/copilot/src/platform/snippy/common/snippyServiceImpl.tscatch (e) { throw e; }re-throws the fetch failure up to the floating callerextensions/copilot/src/platform/snippy/common/snippyFetcher.tsif (fetchResponse.status !== 200) { throw new Error(\Failed with status ${status} ...`); }`extensions/copilot/src/platform/snippy/common/snippyCompute.tsMinTokenLength = 65— client pre-check floor is looser than the service's 110-token floor, so short sources pass the client gate but are rejected by the serverRepro Steps
runSnippyfireshandlePostInsertion; the snippy/matchrequest returns HTTP 400 (source too short).Deterministic given a source between the two thresholds; otherwise triggered whenever the service rejects the request for any reason.
How the Fix Works
Chosen approach (
nextEditProvider.ts):runSnippynowawaitshandlePostInsertion(...)inside atry/catchand routes any failure tothis._logService.error(e, ...). This attaches a rejection handler at the fire-and-forget boundary that owns the background operation, so the failure is recorded as a handled error through the existing log/telemetry pipeline instead of escaping as an unhandled rejection. This follows the principle of fixing the error at the boundary that owns the best-effort background work — snippy code-referencing is explicitly a best-effort check, so its failures should not appear as unhandled application errors. ThelogService.errorcall preserves telemetry visibility of genuine failures; nothing is silently swallowed.Alternatives considered:
MinTokenLength(65) with the server's 110-token floor — rejected: the exact server threshold is not a stable contract the client can safely hardcode, other non-200 responses would still leak, and it would not fix the underlying missing-rejection-handler defect.snippyServiceImpl.handlePostInsertion(removing the rethrow) — rejected: that hides all fetch failures from callers/telemetry rather than handling them at the fire-and-forget site, and would violate the "never silence errors" principle by dropping them entirely.Recommended Owner
@ulugbekna— dominant recent author ofnextEditProvider.ts(author of the most recent NES/inline-edits commits touching this file, merged intomicrosoft/vscode), making them the natural owner for this fire-and-forget call site in the inline-edits area.Note
This was originally intended as a pull request, but PR creation failed. The changes have been pushed to the branch
fix/snippy-post-insertion-unhandled-rejection-aa290b8d76b80828.Original error: ERR_API: [2026-08-20T15:39:49.753Z] create pull request in microsoft/vscode failed (attempt 1)
Original error: Validation Failed: {"resource":"PullRequest","code":"custom","field":"fork_collab","message":"fork_collab Fork collab can't be granted by someone without permission"} - https://docs.github.com/rest/pulls/pulls#create-a-pull-request
Retryable: false
Suggestion: This error cannot be resolved by retrying. Please check the error details and fix the underlying issue.
To create the pull request manually:
gh pr create --title "fix: handle snippy post-insertion rejection to avoid unhandled error (fixes #331807)" --base main --head vscodebot-pr:fix/snippy-post-insertion-unhandled-rejection-aa290b8d76b80828 --repo microsoft/vscodeShow patch (36 lines)