LSP plugin extension conflict resolves silently — losing plugin appears enabled but server never spawns

Resolved 💬 3 comments Opened Jun 1, 2026 by Jodre11 Closed Jul 6, 2026

Summary

When two LSP plugins register lspServers for the same extensionToLanguage mapping
(e.g., both claim .cs), the harness silently picks one and discards the other. There is
no warning at any surface — no log message, no Doctor diagnostic, no /plugin list
indication, no on-disk artifact. The losing plugin appears installed and enabled but
its server is never spawned.

This is a related-but-distinct concern from #32912 / #27692 (closed for inactivity,
asking for multi-server-per-language support). Even if multi-server support is out of
scope, the silent first-wins behaviour is a UX bug independent of that: users
cannot tell whether their plugin is broken, their tool installation is broken, or
something else is winning the race. The diagnostic gap costs hours.

The closing comment on #32912 explicitly invites a fresh issue if still relevant —
filing this as a focused, narrower bug.

Reproduction (real-world, observed 2026-06-01)

  1. Enable both plugins for .cs:
  • csharp-lsp@claude-plugins-official (built-in, uses deprecated csharp-ls)
  • roslyn-lsp@jodre11-plugins, a re-host of

unsafePtr/ClaudeCodeRoslynLspProxy
(uses Microsoft Roslyn LSP)

  1. Open a .cs file in any multi-project solution.
  2. Invoke the LSP findReferences tool.

Expected (one of):

  • Both servers spawn (multi-server, per #32912), OR
  • A warning surfaces clearly indicating the conflict and which server won, OR
  • The user-facing surfaces (/plugin list, status line, /doctor) flag the inactive plugin.

Actual:

  • pgrep -af csharp-ls → running.
  • pgrep -af ClaudeCodeRoslynLspProxy → not running.
  • findReferences returns file-scoped results (Roslyn would have returned solution-wide).
  • No log message indicating the conflict — checked Claude Code's runtime logs and the

per-plugin LSP working dirs.

  • /plugin list shows both plugins as enabled.
  • The user has no way to know the wrong server is serving requests.

The broken state persists across restarts until the user manually flips
csharp-lsp@claude-plugins-official: false in ~/.claude/settings.json. Discovering
that requires reading the source of the plugin manifest format and noticing the
extensionToLanguage overlap — not a reasonable expectation for end users.

Why this matters separately from multi-server support

The C# case is illustrative: shipping a built-in csharp-lsp (using deprecated
csharp-ls) means every C# user installing a more capable third-party plugin
silently has the worse server win
. The same pattern applies to anyone using a
better TypeScript LSP than the official one, a third-party Python server, etc.

Even if Anthropic's strategic answer is "one server per extension is the design,"
the silent part is the bug. A line like
[lsp] WARN: extension '.cs' is registered by 2 enabled plugins (csharp-lsp@claude-plugins-official, roslyn-lsp@jodre11-plugins). Using csharp-lsp; disable the other to switch.
on first plugin resolution would have saved the entire investigation.

Concrete asks (any one of these would close the gap)

  1. Log a warning when extension registration is contested, naming both plugins

and which one was selected.

  1. Surface the conflict in /plugin list and /doctor as a soft warning beside

the losing plugin.

  1. Document the resolver order so users at least know what to expect (currently

ambiguous whether it's marketplace order, install order, alphabetical, or
something else).

Environment

  • Claude Code: 2.1.158 (Bedrock)
  • macOS Darwin 25.5.0
  • Two enabled .cs LSP plugins as described above

Related

  • #32912 (closed 2026-05-30 for inactivity) — multi-server-per-language feature
  • #27692 (closed earlier, also inactivity) — original duplicate
  • #53399 — .lsp.json location quirk for third-party plugins
  • #33552 — .lsp.json settings field not propagated

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗