fix: do not surface transient server 5xx as unhandled error in NES fetch (fixes #324972)#324987
Open
vs-code-engineering[bot] wants to merge 1 commit into
Open
Conversation
…tch (fixes #324972) Reclassify ChatFetchResponseType.Failed (transient HTTP 5xx, e.g. "Server error: 500") from NoNextEditReason.FetchFailure to NoNextEditReason.Uncategorized in mapChatFetcherErrorToNoNextEditReason. FetchFailure is re-thrown by nextEditProvider and escalated to onUnexpectedError by the background inline-completion caller, polluting error telemetry with transient server errors. Uncategorized is non-throwing and is grouped into the same NES telemetry bucket, so the signal is preserved without escalating a transient 5xx into an unhandled error. The error is still logged at the fetch layer (chatMLFetcher). 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
A background Next Edit Suggestion (NES) fetch that receives a transient HTTP 5xx (e.g.
Server error: 500) is classified as a throwingNoNextEditReason.FetchFailure, re-thrown deep innextEditProvider, and then escalated toonUnexpectedErrorby the fire-and-forget inline-completion caller. The result is unhandled-error telemetry of the formunhandlederror - An unexpected error occurred: {"type":"failed","reason":"Server error: 500",...}for what is really a transient, already-logged server condition. Impact: telemetry noise (matching therecent-regression/stable-anomalylabels) with no user-facing failure beyond a skipped suggestion.Fixes #324972
Recommended reviewer:
@ulugbeknaCulprit Commit
No single culprit commit identified.
extensions/copilot/src/extension/xtab/node/xtabProvider.tsis a vendored file synced frommicrosoft/vscode-copilot-chat; itsmicrosoft/vscodehistory is composed of feature-sync commits, and theFailed → FetchFailuremapping insidemapChatFetcherErrorToNoNextEditReasonis long-standing — it is present at the shipped commitfc3def67(extension0.55.0) reported in the issue.This is a latent misclassification, not re-bucketing: no commit changed the error message text to synthesize a new bucket. The
recent-regression/stable-anomalysignals most plausibly reflect a server-side HTTP 5xx spike raising the trigger rate of a pre-existing defect rather than a code regression. Telemetry history endpoints returned no per-release rows (get_bucket_historyempty;get_sample_raw_event_for_bucketreportedfingerprint_missing_lastSeen), so a precise regression window could not be established. The fix corrects the classification at the producer regardless of when the spike began.Code Flow
sequenceDiagram participant Server as Copilot API participant Fetcher as chatMLFetcher participant Classify as mapChatFetcherError... participant NextEdit as nextEditProvider participant Caller as inlineCompletionProvider Server->>Fetcher: HTTP 500 Note over Fetcher: logService.error("Server error: 500")<br/>returns ChatFetchResponseType.Failed Fetcher->>Classify: Failed Note over Classify: ⚠️ Root cause:<br/>Failed mapped to FetchFailure (throwing group) Classify->>NextEdit: NoNextEditReason.FetchFailure Note over NextEdit: throw error.error (L487) NextEdit->>Caller: rejected promise Note over Caller: 💥 raceAndAll(..., onUnexpectedError)<br/>unhandled-error telemetryAffected Files
extensions/copilot/src/extension/xtab/node/xtabProvider.tscase ChatFetchResponseType.Failed:now returnsNoNextEditReason.Uncategorizedinstead ofFetchFailureextensions/copilot/src/extension/inlineEdits/node/nextEditProvider.tsthrow error.errorforFetchFailure/Unexpectedextensions/copilot/src/extension/inlineEdits/vscode-node/inlineCompletionProvider.tsraceAndAll([...getNextEdit()...], onUnexpectedError)extensions/copilot/src/extension/inlineEdits/vscode-node/raceAndAll.tserrorHandler(error)invoked on every rejectionextensions/copilot/src/extension/prompt/node/chatMLFetcher.tsServer error: ${response.status}then_logService.error(reason)(unchanged)extensions/copilot/src/platform/inlineEdits/common/statelessNextEditProvider.tsFetchFailure, L302Uncategorized; L484 groups both into the same NES telemetry bucketextensions/copilot/src/extension/xtab/test/node/xtabProvider.spec.tsFailedmoved into the "maps $type to Uncategorized" assertion groupRepro Steps
Non-deterministic — depends on the server returning a transient 5xx.
unhandlederror - An unexpected error occurred: {"type":"failed","reason":"Server error: 500",...}with aTimeout._onTimeout → onUnexpectedErrorstack top — the transient 500 was escalated to an unhandled error rather than being silently skipped.How the Fix Works
Chosen approach
extensions/copilot/src/extension/xtab/node/xtabProvider.ts— inmapChatFetcherErrorToNoNextEditReason,case ChatFetchResponseType.Failed:is moved from theNoNextEditReason.FetchFailuregroup (throwing) into theNoNextEditReason.Uncategorizedgroup (non-throwing), alongside the other server-side conditions such as rate limiting and quota. This corrects the defect at the data producer — the function that turns a fetch outcome into aNoNextEditReason— rather than at the crash site (thethrowinnextEditProvider). BecausenextEditProviderre-throws onlyFetchFailure/Unexpected, a transient 5xx classified asUncategorizedno longer reaches thethrow, and therefore never reachesonUnexpectedError. The signal is preserved, not silenced: the fetch error is still logged atchatMLFetchervialogService.error(untouched), andUncategorizedis grouped withFetchFailurein the same NES telemetry bucket (statelessNextEditProviderL484), so failure tracking continues. Notry/catchwas added and no logging was removed.extensions/copilot/src/extension/xtab/test/node/xtabProvider.spec.ts— theFailedentry is moved from the "maps to FetchFailure"it.eachblock to the "maps to Uncategorized" block to lock in the corrected classification.Why this fix: after this change,
mapChatFetcherErrorToNoNextEditReasonreturnsNoNextEditReason.UncategorizedforChatFetchResponseType.Failed, sonextEditProvider.ts:485-487can no longer re-throw a transient server 5xx as aFetchFailure, and theraceAndAll(..., onUnexpectedError)caller never escalates it into unhandled-error telemetry.Alternatives considered
try-catchat thethrowsite innextEditProvider— rejected: that is the crash site, it would blanket-suppress everyFetchFailure, and it hides the classification defect instead of fixing the producer.raceAndAllcaller — rejected: violates the never-silence-with-try/catchprinciple and would suppress genuine errors too.BadRequest(400),NotFound(404),NetworkError, andUnknown— rejected: out of scope; 400/404/Unknowncan indicate genuine client-side bugs worth surfacing, and there is no telemetry evidence they belong to this bucket.Recommended Owner
@ulugbekna— owns "NES (Next Edit Suggestions) extension side (ie, NOT core/UI side)" in the teamworking-areas.md, an exact match for the fix location underextensions/copilot/src/extension/xtab/. Active and a team member with write access: multiple NES commits authored directly tomicrosoft/vscodedated 2026-07-08 (well within the 90-day liveness window).