fix(completion): timeout + CancellationToken for pom IntelliSense network calls (#1127)#1170
Merged
Merged
Conversation
…telliSense network calls The pom.xml completion provider (PomCompletionProvider) calls search.maven.org via `src/utils/requestUtils.ts#httpsGet`. That helper had: - no socket / request timeout, so a stuck TCP connection (corporate proxy, DNS black hole, unreachable Maven Central) would leave the underlying Promise pending forever; - no request-level error handler, only a response-level one; - no awareness of VS Code's `CancellationToken`, so even when the editor wanted to cancel a completion request the in-flight HTTPS call kept running. Combined with the previous serial `await provider.provide(...)` loop in `PomCompletionProvider.provideCompletionItems`, a single hung HTTPS call made the entire IntelliSense popup stay on `Loading...` indefinitely (see #1127). This change: - adds a 10s socket timeout to `httpsGet` and wires `req.on('timeout')` to `req.destroy(...)`; - adds a request-level `error` handler and a single `settle` guard so the Promise can never resolve and reject; - threads an optional `CancellationToken` through `getArtifacts` / `getVersions` down into `httpsGet` and aborts the request via `req.destroy()` on cancellation; - in `PomCompletionProvider` honors the `CancellationToken` and runs the registered sub-providers concurrently with `Promise.all` + a per-provider try/catch, so a slow / failing provider can no longer block the others; - in `ArtifactProvider` switches the central / index / local fan-out to `Promise.allSettled` so a network failure on Maven Central still returns local and index results instead of dropping everything. No behavior change when the network is healthy: completions still come from all three sources. When the network hangs or is unreachable, the popup now resolves within 10s instead of getting stuck. Refs #1127 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wenytang-ms
requested review from
chagong,
jdneo and
testforstephen
as code owners
May 27, 2026 05:25
Follow-up review fixes for #1127: 1. Introduce a dedicated CancellationError class. httpsGet now rejects with this class on token cancellation and request timeout cleanup, and getArtifacts/getVersions suppress console.error for it. VS Code cancels completion tokens on every keystroke, so without this every superseded request was logging "Error: Cancelled" to the extension output. 2. Document the (intentional) behavior change in getArtifacts / getVersions: the try/catch now wraps httpsGet too, so network errors are swallowed and return [] instead of propagating to command handlers like addDependencyHandler / setDependencyVersionHandler. This produces an empty quick pick on outage rather than an unhandled rejection, which is the safer UX. Noted in PR description. 3. Close a tiny race where cancelSub could be registered after the request had already errored synchronously, leaving the CancellationToken listener undisposed. We now re-check `settled` immediately after subscribing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Hardens the pom.xml IntelliSense completion pipeline so that slow/unreachable Maven Central (or other network pathologies) can’t leave VS Code completion stuck on Loading... indefinitely, by adding timeouts/cancellation and making provider fan-out resilient to partial failures.
Changes:
- Add timeout, request-level error handling, and optional
CancellationTokensupport to the HTTPS helper used for Maven Central queries. - Run pom completion sub-providers concurrently, while honoring VS Code cancellation and isolating failures so one provider can’t block the others.
- Fan out Central/Index/Local artifact suggestions via
Promise.allSettledso local/index results still appear when Central fails.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/requestUtils.ts | Adds HTTPS timeout, single-settle guarding, request error handling, and optional cancellation support for Maven Central calls. |
| src/completion/PomCompletionProvider.ts | Runs completion providers concurrently and honors CancellationToken to avoid returning stale results. |
| src/completion/providers/IXmlCompletionProvider.ts | Extends provider interface to optionally accept a cancellation token. |
| src/completion/providers/ArtifactProvider.ts | Uses Promise.allSettled to aggregate Central/Index/Local results without one failure blocking all completions. |
| src/completion/providers/artifact/IArtifactProvider.ts | Plumbs optional CancellationToken through artifact provider APIs. |
| src/completion/providers/artifact/FromCentral.ts | Forwards token to Maven Central query helpers so in-flight requests can be aborted on cancellation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Follow-up for PR #1170 (issue #1127). Adds 9 focused unit tests for `getArtifacts` / `getVersions` that drive the underlying `httpsGet` state machine through proxyquire-stubbed `https`: - short-keyword short-circuit (no HTTPS call) - successful 200 response → parsed docs - `options.timeout` set to HTTPS_GET_TIMEOUT_MS (10s) - pre-cancelled token → short-circuit, no console.error - cancellation mid-flight → `req.destroy`, no console.error - non-cancellation network error → returns [] AND logs - socket idle `timeout` event → req destroyed with timeout error, logs - `getVersions` success and silent cancellation paths Also fixes the test harness so new `.test.ts` files load via `ts-node/register/transpile-only` instead of Node's `--experimental-strip-types` ESM loader (which is on by default in Node 22.18+ and broke `require()` inside fresh test files). The fix is one line in .mocharc.json: `"node-option": ["no-experimental-strip-types"]`. All 30 unit tests pass (21 pre-existing + 9 new). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI runs the unit tests on Node 20, which does not understand the `--no-experimental-strip-types` flag I added in 88eba6a — the previous commit broke `npm run test:unit` on all three CI runners with: bad option: --no-experimental-strip-types Drop the unconditional `node-option` from .mocharc.json and wrap mocha in scripts/run-unit-tests.js, which only forwards the flag on Node 22.6+ (where `--experimental-strip-types` actually exists and is the cause of the harness quirk for newly added .test.ts files). Node 20 runs mocha exactly as it did before this PR. Verified locally on Node 22.21.1: 30 tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
Author
chagong
approved these changes
May 28, 2026
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.



What
Defensive hardening of the pom.xml IntelliSense completion path so that an unreachable / slow Maven Central no longer leaves the suggestions popup stuck on
Loading...forever.Refs #1127.
Why
In #1127 a user reports that
Ctrl+Spacebrings up the suggestions popup but it stays onLoading...indefinitely. We've asked for more info to confirm the root cause (pom.xml vs Java file, network environment, logs), but regardless of the exact root cause in their setup, the existing completion code has a real "infinite Loading…" hazard worth fixing.Tracing the pom completion provider:
src/extension.tsregistersPomCompletionProviderforpom.xmlwith trigger characters.,-,<.PomCompletionProvider.provideCompletionItemspreviouslyawaited each sub-provider serially and ignored theCancellationTokenVS Code passes in.ArtifactProviderfan-out (Maven Central / index / local) was also fully serial: a slow source blocked all the others.src/utils/requestUtils.tshad:errorhandler, only a response-level one;CancellationToken.Combined, that means one hung HTTPS call to
search.maven.orgis enough to make the completion popup stay onLoading...until VS Code is restarted — matching the symptom in #1127.What changed
src/utils/requestUtils.tsoptions.timeout+req.on('timeout')→req.destroy(...)).errorhandler.settleguard so the Promise can only resolve or reject once.vscode.CancellationTokenand aborts the request viareq.destroy()on cancellation.CancellationErrorclass so that completion-side cancellations (which fire on every keystroke as VS Code supersedes the previous token) do not spamconsole.error.getArtifacts/getVersionssuppress logging for that class only.settledflag immediately after subscribing totoken.onCancellationRequested, closing a tiny race where a synchronous error fromhttps.getcould leave the cancellation listener undisposed.src/completion/PomCompletionProvider.tsCancellationToken(early-out before parsing the doc, and after fan-out).Promise.all+ per-providercatchso one failing provider can no longer block the others.src/completion/providers/ArtifactProvider.tsPromise.allSettled: a network failure on Maven Central still returns local and index results.FromCentral(the only sub-provider that performs real network I/O —FromIndexis an in-process JDT.LS RPC andFromLocalis afast-globover~/.m2/repository; neither has a public abort API).src/completion/providers/IXmlCompletionProvider.ts/IArtifactProvider.ts/FromCentral.tstokenparameter plumbing. Existing implementations remain compatible.Behavior summary
Loading...indefinitely.Intentional behavior change in
getArtifacts/getVersionsThe existing
try/catchin these helpers only wrappedJSON.parse. I widened thetryto also wrap theawait httpsGet(...)call. Net effect:httpsGetpropagated out ofgetArtifacts/getVersions. The completion path tolerated this (it caught at a higher level), but the command-handler callers (src/handlers/addDependencyHandler.ts,src/handlers/setDependencyVersionHandler.ts) would surface an unhandled rejection.[]. Command handlers now show an empty quick pick instead of failing, which is the safer UX when Maven Central is unreachable.This is intentional but worth calling out for reviewers.
Tests
Added
test/unit/requestUtils.test.ts(9 cases) covering the changed behavior. Tests stubhttpsviaproxyquireand drive the underlying state machine throughdata/end/error/timeoutevents:[]options.timeoutconsole.errorreq.destroycalled, noconsole.errorECONNREFUSED)[], does logtimeouteventgetVersionssuccessgetVersionscancel[].mocharc.jsonalso gains"node-option": ["no-experimental-strip-types"]to work around a Node 22.18+ default that caused newly created.test.tsfiles to be loaded by Mocha through Node's ESM strip-types path instead of the pre-existingts-node/register/transpile-onlyCommonJS hook (sorequire()was undefined in the new file). With the flag set, all.test.tsfiles load uniformly via ts-node.Validation
npm run compile— clean.npm run tslint— no new warnings.npm run test:unit— 30 passing (21 pre-existing + 9 new).npm run webpack— bundles successfully.Notes for reviewer
getLatestVersionandfetchPluginMetadataXmlalso usehttpsGetand automatically get the new 10 s timeout. I intentionally did not change their public signatures (no caller passes a token today).maven.completion.timeoutMsbut kept it as a constant for now to minimize surface area; happy to expose it as a setting if reviewers want.jdtls.ext/com.microsoft.java.maven.plugin). If Noticed IntelliSense stopped working #1127 turns out to be Java-side IntelliSense rather than pom.xml IntelliSense, the actual root cause likely lives there or inredhat.java/ JDT.LS, and we'll need the additional info requested on the issue.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com