Skip to content

Ensure the language client is always ready before using it - #14617

Merged
Bob Brown (bobbrow) merged 18 commits into
mainfrom
bobbrow/languageClientRefactor
Aug 25, 2026
Merged

Ensure the language client is always ready before using it#14617
Bob Brown (bobbrow) merged 18 commits into
mainfrom
bobbrow/languageClientRefactor

Conversation

@bobbrow

@bobbrow Bob Brown (bobbrow) commented Jul 29, 2026

Copy link
Copy Markdown
Member

There are some crash reports indicating that some events attempting to communicate with cpptools are firing before the language client is ready to service them. While investigating, I noticed that several events are not awaiting the ready event.

This PR wraps the LanguageClient interface in a class that ensures the client is ready before making any RPC calls into cpptools. Some other minor cleanup in the provider classes is included. I did not root out all places where DefaultClient.ready is being unnecessarily awaited as it will require more testing, but those remaining are harmless.

Note: I did not see a need to keep the enqueue functionality based on my findings. On the native side, we already defer all messages in a queue until configuration happens so we can let the messages come in the order they are queued on the TypeScript side. Historically, I remember there being a need for "blocking" the language client "Middleware" (protocolFilter.ts), but that was already removed a while ago. enqueue is incompatible with this change as it results in deadlocked awaiting (an enqueued task that makes an RPC call to cpptools).

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.

Pull request overview

This PR introduces a TypeScript wrapper around the VS Code LanguageClient to ensure RPC calls into cpptools are gated on a single “ready” signal, aiming to prevent crashes caused by early event/RPC usage before the language client is usable.

Changes:

  • Added a LanguageClient wrapper that awaits readiness for sendRequest/sendNotification, and updated the core client startup flow to use it.
  • Removed many explicit await client.ready call sites across providers/commands, relying on the wrapper to enforce readiness.
  • Removed the enqueue-based sequencing from client/extension event wiring and performed minor provider cleanup.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Extension/src/LanguageServer/Providers/workspaceSymbolProvider.ts Constructor simplification; relies on centralized readiness behavior.
Extension/src/LanguageServer/Providers/semanticTokensProvider.ts Formatting-only style adjustment.
Extension/src/LanguageServer/Providers/renameProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/onTypeFormattingEditProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/HoverProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/foldingRangeProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/findAllReferencesProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/documentSymbolProvider.ts Switches to using client.languageClient.sendRequest without manual readiness await.
Extension/src/LanguageServer/Providers/documentRangeFormattingEditProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/documentFormattingEditProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/CopilotHoverProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/codeActionProvider.ts Removes explicit readiness await in favor of wrapper.
Extension/src/LanguageServer/Providers/callHierarchyProvider.ts Removes explicit readiness awaits in favor of wrapper.
Extension/src/LanguageServer/protocolFilter.ts Stops manually awaiting client.ready before sending visible-editor updates.
Extension/src/LanguageServer/languageClient.ts New wrapper class that gates requests/notifications on a “ready” signal.
Extension/src/LanguageServer/extension.ts Removes enqueue usage from editor event hooks; removes explicit ready awaits for some commands.
Extension/src/LanguageServer/codeAnalysis.ts Swaps to wrapper LanguageClient and adjusts languageclient imports accordingly.
Extension/src/LanguageServer/clientCollection.ts Removes await newClient.ready during crash-recovery ownership transfer.
Extension/src/LanguageServer/client.ts Central refactor to use wrapper, remove enqueue, and shift readiness responsibility to the wrapper.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Extension/src/LanguageServer/client.ts
Comment thread Extension/src/LanguageServer/client.ts Outdated
@Colengms

Copy link
Copy Markdown
Contributor

With these changes, I'm seeing an unusual number of didChangeSettings being delivered at startup. (Often as many as 6). It may be an existing issue, as I sometimes see up to 3 without your changes.

LSP: (received) cpptools/preinitialize (id: 1)
LSP: (invoked) cpptools/preinitialize (id: 1)
LSP: Sending response (id: 1)
LSP: (received) cpptools/initialize (id: 2)
LSP: (invoked) cpptools/initialize (id: 2)
cpptools version (TypeScript): 1.33.4-main
cpptools version (native): 1.33.5.0
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
LSP: Sending response (id: 2)
LSP: (received) cpptools/didChangeSettings
LSP: (invoked) cpptools/didChangeSettings
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
Enhanced Colorization is enabled.
LSP: (received) cpptools/didChangeSettings
LSP: (invoked) cpptools/didChangeSettings
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
Enhanced Colorization is enabled.
LSP: (received) cpptools/didChangeSettings
LSP: (invoked) cpptools/didChangeSettings
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
Enhanced Colorization is enabled.
LSP: (received) cpptools/didChangeSettings
LSP: (invoked) cpptools/didChangeSettings
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
Enhanced Colorization is enabled.
LSP: (received) cpptools/didChangeSettings
LSP: (invoked) cpptools/didChangeSettings
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
Enhanced Colorization is enabled.
LSP: (received) cpptools/didChangeSettings
LSP: (invoked) cpptools/didChangeSettings
LSP: (received - deferred) cpptools/didChangeVisibleTextEditors
Autocomplete is enabled.
Error squiggles are enabled.
Hover is enabled.
IntelliSense Engine = default.
LSP: (received - deferred) cpptools/didChangeVisibleTextEditors
Enhanced Colorization is enabled.
LSP: (received - deferred) cpptools/didChangeVisibleTextEditors
LSP: (received) cpptools/queryCompilerDefaults (id: 3)
LSP: (invoked) cpptools/queryCompilerDefaults (id: 3)
Compiler info database not connected - skipping load.
Compiler info database not connected - skipping load.
LSP: Sending response (id: 3)
Processed c_cpp_properties.json in 0.0054666s
LSP: (received) cpptools/didChangeCppProperties
LSP: (invoked) cpptools/didChangeCppProperties

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.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

Extension/src/LanguageServer/client.ts:3116

  • restartIntelliSenseForFile reads this.languageClient.code2ProtocolConverter before any readiness await. If the language client wrapper hasn’t been initialized yet, that getter can throw synchronously during argument evaluation, bypassing the .catch(...) and crashing the event path. Await readiness before accessing the converters so the call is reliably deferred until the RPC client exists.
    public async restartIntelliSenseForFile(document: vscode.TextDocument): Promise<void> {
        return this.languageClient.sendNotification(RestartIntelliSenseForFileNotification, this.languageClient.code2ProtocolConverter.asTextDocumentIdentifier(document)).catch(logAndReturn.undefined);

Extension/src/LanguageServer/protocolFilter.ts:49

  • cppEditors is captured before the language client is ready, but onDidChangeVisibleTextEditors builds params synchronously and only awaits readiness inside sendNotification. If initialization is still in progress, the visible editor/selection snapshot may be stale by the time the notification is actually sent. Defer collecting editors until after client.ready resolves (as before) so the server receives a current snapshot.
                    const cppEditors: vscode.TextEditor[] = vscode.window.visibleTextEditors.filter(e => util.isCpp(e.document));
                    void client.onDidChangeVisibleTextEditors(cppEditors).catch(logAndReturn.undefined);

Extension/src/LanguageServer/extension.ts:192

  • The onDidChangeTextEditorVisibleRanges / onDidChangeVisibleTextEditors handlers are async and can reject, but their returned Promises are currently ignored by the event registration callbacks. If either throws/rejects, it can surface as an unhandled promise rejection. Wrap the calls with void ... .catch(logAndReturn.undefined) like other event hooks in this file.
    disposables.push(vscode.window.onDidChangeTextEditorVisibleRanges(e => onDidChangeTextEditorVisibleRanges(e)));
    disposables.push(vscode.window.onDidChangeActiveTextEditor(e => onDidChangeActiveTextEditor(e)));
    ui.didChangeActiveEditor(); // Handle already active documents (for non-cpp files that we don't register didOpen).
    disposables.push(vscode.window.onDidChangeTextEditorSelection(e => onDidChangeTextEditorSelection(e)));
    disposables.push(vscode.window.onDidChangeVisibleTextEditors(e => onDidChangeVisibleTextEditors(e)));

@bobbrow

Bob Brown (bobbrow) commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

With these changes, I'm seeing an unusual number of didChangeSettings being delivered at startup. (Often as many as 6). It may be an existing issue, as I sometimes see up to 3 without your changes.

I can check...

EDIT: I did not reproduce this problem, but put out a separate PR to filter unnecessary settings change events which dropped my count down to 0.

@bobbrow
Bob Brown (bobbrow) marked this pull request as ready for review August 14, 2026 16:02
@bobbrow
Bob Brown (bobbrow) requested a review from a team as a code owner August 14, 2026 16:02
@sean-mcmanus

Copy link
Copy Markdown
Contributor

Bob Brown (@bobbrow) Do you want us to try to check this in?

@bobbrow

Copy link
Copy Markdown
Member Author

Bob Brown (Bob Brown (@bobbrow)) Do you want us to try to check this in?

Yes, I was hoping it would get a review and sign off for 1.34.0.

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 (agent8): I found one additional startup-ordering issue on the current head.

Comment thread Extension/src/LanguageServer/client.ts Outdated

@sean-mcmanus Sean McManus (sean-mcmanus) 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.

1.34.1...or 1.34.0 if it can get in in time (since 1.34.0 was delayed).

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.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Comment thread Extension/src/LanguageServer/client.ts Outdated
Comment thread Extension/src/LanguageServer/languageClient.ts

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.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

Extension/src/LanguageServer/extension.ts:580

  • The wrapper cannot replace this readiness wait because promptSelectIntelliSenseConfiguration() performs no RPC before checking compilerDefaults; during startup that value is still undefined and the command silently returns (client.ts:1286–1289). Restore the wait so invoking the command while initialization is in progress opens the picker after compiler discovery instead of doing nothing.
    return clients.ActiveClient.promptSelectIntelliSenseConfiguration(sender);

Comment thread Extension/src/LanguageServer/client.ts

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 (agent8): One suppressed startup-timing observation remains valid on the current head.

Comment thread Extension/src/LanguageServer/extension.ts
@bobbrow
Bob Brown (bobbrow) merged commit ba48a2c into main Aug 25, 2026
6 checks passed
@bobbrow
Bob Brown (bobbrow) deleted the bobbrow/languageClientRefactor branch August 25, 2026 18:51
@github-project-automation github-project-automation Bot moved this from Pull Request to Done in cpptools Aug 25, 2026
Sean McManus (sean-mcmanus) added a commit that referenced this pull request Sep 9, 2026
* Use npm ci in issue workflows (#14702)

* Pin the Yarn bootstrap and add SHA-512 lock checksums (#14703)

* Add SHA-512 checksums to yarn.lock

* Pin the Yarn bootstrap install

* Register LLVM component for LLDB-MI (#14704)

* Add xobjgen to gitignore (for Linux/Mac). (#14707)

* Retry transient Yarn install failures (#14708)

* Ensure the language client is always ready before using it (#14617)

* Update 1.34.0 changelog (#14710)

* Update changelog and version for 1.34.1 (#14714)

* Update clang-tidy checks to 23.1.0 (#14713)

* Use native file type mappings for TypeScript-side classification (#14711)

* Add 1.34.2 changelog (#14722)

* Add 1.34.2 changelog

* Remove ignored network isolation policy (#14728)

* Bump fast-uri from 3.1.5 to 3.1.6 in /ExtensionPack (#14731)

Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.5 to 3.1.6.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](fastify/fast-uri@v3.1.5...v3.1.6)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @xmldom/xmldom from 0.8.13 to 0.8.15 in /Extension (#14733)

Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.8.13 to 0.8.15.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](xmldom/xmldom@0.8.13...0.8.15)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-version: 0.8.15
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump browserslist from 4.28.1 to 4.28.8 in /Extension (#14732)

Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.28.1 to 4.28.8.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](browserslist/browserslist@4.28.1...4.28.8)

---
updated-dependencies:
- dependency-name: browserslist
  dependency-version: 4.28.8
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Remove unused gulp-sourcemaps dependency (#14729)

* Remove unused gulp-sourcemaps dependency

* Remove orphaned dependency resolutions

* Fix custom configuration provider regression (#14725)

* Restore custom configuration provider checks

* Ignore empty crash report files (#14730)

* Ignore empty crash report files

* Preserve pending crash reads across clients

* Keep crash writing state for pending reports

* Fix fast-uri dependency. (#14735)

* Fix fast-uri dependency.

* Also for Themes.

* Implement session state tracking for "Run and Debug" button when Inte… (#14719)

* Implement session state tracking for "Run and Debug" button when IntelliSense is disabled and add corresponding tests

* Enhance session state tracking for build and debug by updating folder open status and adding tests

* Fix build and debug folder session state tracking

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sean McManus <seanmcm@microsoft.com>

* Fix #11263: Make Edit Configurations UI fully theme-aware in custom themes (#14692)

* Fix #11263: use theme-aware colors in Edit Configurations UI

* Enhance dropdown styling with theme-aware colors in settings UI

---------

Co-authored-by: Sean McManus <seanmcm@microsoft.com>

* Add processFilter for remote attach process selection (#14684)

* Add processFilter for remote attach process selection

When attaching to a process on a remote target, the process always has
to be selected by hand, even though the launch configuration already
knows which executable it belongs to. A generated configuration cannot
hard-code processId either, because the pid changes on every boot and
on every restart of the service, so the picker is the only option.

Add an optional processFilter regular expression to the cppdbg attach
configuration. When set, it is matched against the label, description
and detail of the remote process list:

  exactly one match  attach to that process directly
  more than one      show the picker with only the matching entries
  no match           show the full picker, as before

All three fields are considered because the item format depends on the
transport: useExtendedRemote reports the user and the full command line
in the label, while pipeTransport reports the process name in the label
and the command line in the detail.

An invalid regular expression is reported instead of being silently
ignored.

This affects remote attach only (pipeTransport and useExtendedRemote);
local attach continues to use program-based matching.

Closes #14682

* Extract remote process filtering into a helper

Move the matching logic out of RemoteAttachPicker into a standalone
function so that it can be unit tested without a VS Code quick pick or
a live connection to a remote target.

No functional change.

* Add unit tests for processFilter matching

Cover empty and non-string filter values, matching against label,
description and detail, multiple matches, an invalid regular
expression, and a regression case ensuring missing fields are not
treated as empty strings.

---------

Co-authored-by: Adrian Freihofer <adrian.freihofer@siemens.com>
Co-authored-by: Sean McManus <seanmcm@microsoft.com>

* Bump the github-actions group with 2 updates (#14738)

Bumps the github-actions group with 2 updates: [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.37.7 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@ff2f1c6...cdf488f)

Updates `github/codeql-action/analyze` from 4.37.7 to 4.37.9
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@ff2f1c6...cdf488f)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.37.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Fix localization translation errors (#14737)

* Localization - Translated Strings

* Fix localization translation errors

* Address localization review feedback

* Fix localization review feedback

---------

Co-authored-by: csigs <csigs@users.noreply.github.com>

* Update changelog for 1.34.3 (#14740)

* Update changelog and version for 1.34.4. (#14748)

* Enable PR CI for release and insiders (#14751)

* Fix localization string import (#14743)

* Fix localization string import
* Correct conversion cycle translations

* Add Run and Debug session state tests (#14736)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Bob Brown <bobbrow@users.noreply.github.com>
Co-authored-by: Colen Garoutte-Carson <49173979+Colengms@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Prashant Kumar Rai <prashant.kumar2021@vitbhopal.ac.in>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: afreof <adrian.freihofer@gmail.com>
Co-authored-by: Adrian Freihofer <adrian.freihofer@siemens.com>
Co-authored-by: csigs <csigs@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants