Skip to content

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
mainfrom
fix/nes-transient-5xx-unhandled-error-324972-8d89a46499fd2e41
Open

fix: do not surface transient server 5xx as unhandled error in NES fetch (fixes #324972)#324987
vs-code-engineering[bot] wants to merge 1 commit into
mainfrom
fix/nes-transient-5xx-unhandled-error-324972-8d89a46499fd2e41

Conversation

@vs-code-engineering

Copy link
Copy Markdown
Contributor

Summary

A background Next Edit Suggestion (NES) fetch that receives a transient HTTP 5xx (e.g. Server error: 500) is classified as a throwing NoNextEditReason.FetchFailure, re-thrown deep in nextEditProvider, and then escalated to onUnexpectedError by the fire-and-forget inline-completion caller. The result is unhandled-error telemetry of the form unhandlederror - An unexpected error occurred: {"type":"failed","reason":"Server error: 500",...} for what is really a transient, already-logged server condition. Impact: telemetry noise (matching the recent-regression/stable-anomaly labels) with no user-facing failure beyond a skipped suggestion.

Fixes #324972
Recommended reviewer: @ulugbekna

Culprit Commit

No single culprit commit identified.

extensions/copilot/src/extension/xtab/node/xtabProvider.ts is a vendored file synced from microsoft/vscode-copilot-chat; its microsoft/vscode history is composed of feature-sync commits, and the Failed → FetchFailure mapping inside mapChatFetcherErrorToNoNextEditReason is long-standing — it is present at the shipped commit fc3def67 (extension 0.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-anomaly signals 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_history empty; get_sample_raw_event_for_bucket reported fingerprint_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 telemetry
Loading

Affected Files

File Role Evidence
extensions/copilot/src/extension/xtab/node/xtabProvider.ts root cause (fixed) L1758-L1772: case ChatFetchResponseType.Failed: now returns NoNextEditReason.Uncategorized instead of FetchFailure
extensions/copilot/src/extension/inlineEdits/node/nextEditProvider.ts throw site L485-L487: throw error.error for FetchFailure/Unexpected
extensions/copilot/src/extension/inlineEdits/vscode-node/inlineCompletionProvider.ts escalation L296-L300: raceAndAll([...getNextEdit()...], onUnexpectedError)
extensions/copilot/src/extension/inlineEdits/vscode-node/raceAndAll.ts escalation mechanism L42: errorHandler(error) invoked on every rejection
extensions/copilot/src/extension/prompt/node/chatMLFetcher.ts error origin + logging L1621-L1623: Server error: ${response.status} then _logService.error(reason) (unchanged)
extensions/copilot/src/platform/inlineEdits/common/statelessNextEditProvider.ts taxonomy + telemetry L275 FetchFailure, L302 Uncategorized; L484 groups both into the same NES telemetry bucket
extensions/copilot/src/extension/xtab/test/node/xtabProvider.spec.ts test update L220: Failed moved into the "maps $type to Uncategorized" assertion group

Repro Steps

Non-deterministic — depends on the server returning a transient 5xx.

  1. Enable Copilot inline edits / NES in the editor.
  2. Edit near the cursor so a background next-edit fetch is triggered.
  3. Have the Copilot model endpoint return HTTP 500 for that fetch (occurs naturally during server-side 5xx spikes; can be simulated by pointing the client at an endpoint that returns 500).
  4. Observe error telemetry: unhandlederror - An unexpected error occurred: {"type":"failed","reason":"Server error: 500",...} with a Timeout._onTimeout → onUnexpectedError stack 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 — in mapChatFetcherErrorToNoNextEditReason, case ChatFetchResponseType.Failed: is moved from the NoNextEditReason.FetchFailure group (throwing) into the NoNextEditReason.Uncategorized group (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 a NoNextEditReason — rather than at the crash site (the throw in nextEditProvider). Because nextEditProvider re-throws only FetchFailure/Unexpected, a transient 5xx classified as Uncategorized no longer reaches the throw, and therefore never reaches onUnexpectedError. The signal is preserved, not silenced: the fetch error is still logged at chatMLFetcher via logService.error (untouched), and Uncategorized is grouped with FetchFailure in the same NES telemetry bucket (statelessNextEditProvider L484), so failure tracking continues. No try/catch was added and no logging was removed.
  • extensions/copilot/src/extension/xtab/test/node/xtabProvider.spec.ts — the Failed entry is moved from the "maps to FetchFailure" it.each block to the "maps to Uncategorized" block to lock in the corrected classification.

Why this fix: after this change, mapChatFetcherErrorToNoNextEditReason returns NoNextEditReason.Uncategorized for ChatFetchResponseType.Failed, so nextEditProvider.ts:485-487 can no longer re-throw a transient server 5xx as a FetchFailure, and the raceAndAll(..., onUnexpectedError) caller never escalates it into unhandled-error telemetry.

Alternatives considered

  • Add a guard/try-catch at the throw site in nextEditProvider — rejected: that is the crash site, it would blanket-suppress every FetchFailure, and it hides the classification defect instead of fixing the producer.
  • Swallow the rejection at the raceAndAll caller — rejected: violates the never-silence-with-try/catch principle and would suppress genuine errors too.
  • Also reclassify BadRequest (400), NotFound (404), NetworkError, and Unknown — rejected: out of scope; 400/404/Unknown can 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 team working-areas.md, an exact match for the fix location under extensions/copilot/src/extension/xtab/. Active and a team member with write access: multiple NES commits authored directly to microsoft/vscode dated 2026-07-08 (well within the 90-day liveness window).

Generated by errors-fix · 3.1K AIC · ⌖ 347.5 AIC · ⊞ 69.8K ·

…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>
Copilot AI review requested due to automatic review settings July 8, 2026 16:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot can't review bot-authored pull requests automatically. A user with Copilot access can request a review manually.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot can't review bot-authored pull requests automatically. A user with Copilot access can request a review manually.

@vs-code-engineering vs-code-engineering Bot marked this pull request as ready for review July 8, 2026 16:33
@vs-code-engineering vs-code-engineering Bot enabled auto-merge (squash) July 8, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Error] [GitHub.copilot-chat] unhandlederror-An unexpected error occurred: {"type":"failed","reason":"Server error: 500","requestI...

2 participants