Skip to content

[BUG] Router gives diagnostics-only servers exclusive ownership of their file extensions, shadowing real language servers #74

@Sentixxx

Description

@Sentixxx

Summary

With both vscode-langservers and vtsls enabled (this marketplace), every LSP request on .ts/.tsx/.js/.jsx is routed to plugin:vscode-langservers:eslint and fails with Unhandled method. vtsls never receives the request.

The eslint sub-server has to claim those extensions in extensionToLanguage to receive document content for diagnostics — but the router treats that claim as exclusive ownership of all textDocument/* requests, so eslint (which only implements publishDiagnostics) shadows every real server on those file types.

Reproduction

/plugin install vtsls@claude-code-lsps
/plugin install vscode-langservers@claude-code-lsps
npm i -g @vtsls/language-server typescript vscode-langservers-extracted
/reload-plugins

Then any LSP request on a .ts / .tsx file:

Error performing hover: LSP request 'textDocument/hover' failed for server
'plugin:vscode-langservers:eslint': Unhandled method textDocument/hover

/reload-plugins reports 4 plugin LSP servers loaded (html, css, eslint, vtsls), confirming vtsls is up but never consulted. Same failure for goToDefinition, documentSymbol, findReferences, goToImplementation, prepareCallHierarchy.

Confirmed with hrsh7th's vscode-langservers-extracted@4.10.0, which still ships the full eslint server (unlike the Zed fork in #62).

Impact

Any user pairing vscode-langservers with a real TS/JS/Vue/Svelte/Astro server loses all non-diagnostic LSP features on those file types. The failure presents as "vtsls doesn't work" (cf. #1). The same shape will reappear with any future diagnostics-only plugin (biome, ruff, dprint, …) coexisting with a typecheck server.

Current workaround

Disable the whole vscode-langservers plugin and /reload-plugins. Cost: html and css LSPs are lost too, since all three sub-servers share one enable switch. Patching the cached .lsp.json to drop eslint's extensionToLanguage unblocks vtsls but silences eslint (it then never receives didOpen), and gets wiped on plugin update — not a real fix without router changes.

Proposed fix

Same model VS Code uses: multiple servers may subscribe to the same file type; dispatch is by capability, with an explicit priority for ties.

  1. Capability-based dispatch. After initialize, route each method only to servers that advertise the matching capability (hoverProvider, definitionProvider, etc.). ESLint LSP does not advertise these — under capability filtering alone, hover/definition/documentSymbol requests on .ts files would skip eslint and land on vtsls. This single change fixes the reported bug.
  2. Priority tiebreaker. When multiple servers advertise the same capability (vtsls + biome both claim hoverProvider), pick by an explicit priority field, higher wins.
  3. Unhandled method fallback. If the chosen server fails or times out, try the next candidate in priority order. Safety net for mis-advertised capabilities.

Key semantic change: extensionToLanguage means "subscribe to documents of these extensions", not "exclusively own them". didOpen / didChange should broadcast to every subscriber; publishDiagnostics aggregates from all sources.

Environment

  • Claude Code 2.1.143
  • vtsls@claude-code-lsps v0.1.0 + runtime vtsls@0.3.0
  • vscode-langservers@claude-code-lsps v0.1.0 + runtime vscode-langservers-extracted@4.10.0
  • Linux container, Node 22.22.2, project: React + Vite + TypeScript

Related

  • LSP not working #1 — early "vtsls doesn't work" report closed without root cause; plausibly the same issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions