[BUG] LSP plugins installed before the lspServers manifest-format change load as silent empty shells; `plugin update` reports the broken artifact as up-to-date
Status Open
Reported on v2.1.212
Maintainer reply ✓ Yes — bcherny
Workaround ✓ Mentioned in thread ↓
Activity 4 comments · opened Jul 17, 2026
💡 Likely answer: A maintainer (bcherny, collaborator)
responded on this thread — see the highlighted reply below.
Summary
LSP plugins installed before the lspServers config moved into the marketplace manifest are silently non-functional: they load as empty shells (no LSP server launched, no error anywhere), and claude plugin update cannot repair them because it compares version strings and pyright-lsp has been 1.0.0 throughout. The only fix is uninstall + install, which nothing tells the user to do.
Related but distinct: #15148 (same symptom on a fresh install, Dec 2025), #61954 (update-says-latest-but-stale, path-based plugins, closed), #41922 (cache ignored marketplace SHA on install, closed — install is indeed fixed; update is not).
Environment
- Claude Code 2.1.212, macOS 26.5.1 (arm64)
pyright-lsp@claude-plugins-official, originally installed 2026-01-29pyright-langserver1.1.411 on PATH (verified present in the Claude Code process environment)
Reproduction / diagnosis
- Plugin installed 2026-01-29;
installed_plugins.jsonshows"version": "1.0.0", "lastUpdated": "2026-01-29"pinned to commitd49ad35, while sibling plugins with"version": "unknown"refreshed themselves continuously. - The Jan-2026 cached artifact at
~/.claude/plugins/cache/claude-plugins-official/pyright-lsp/1.0.0/contains a generated.claude-plugin/plugin.jsonwith onlyname/description/version/author— nolspServerskey. The current marketplace format instead declareslspServersin themarketplace.jsonentry (the plugin source dir itself now ships only LICENSE + README). - Result: plugin "loads" successfully every session,
/pluginshows it enabled with no errors, but nopyright-langserverchild process is ever spawned, no diagnostics are delivered, and noLSPtool is registered. The only visible symptom is the plugin drifting into "Not used recently" (in our case: enabled since January, "not used in 41 days", user assumed it was Claude's choice). claude plugin update pyright-lsp@claude-plugins-official→✔ pyright-lsp is already at the latest version (1.0.0).The version string never changed when the manifest format did, so the updater considers the stale, functionally-broken artifact current.- Fix that works:
claude plugin uninstall+claude plugin install, then restart. The fresh install materializes correctly (no generated plugin.json;claude plugin detailsnow reportsLSP servers (1) pyright), and after restart diagnostics push + theLSPtool both work.
Why this deserves its own issue
- Every user who installed any official LSP plugin before the manifest change is in this state today, and nothing in the product surfaces it: no load error, no
/pluginErrors entry (the binary-missing case does produce one; this case does not), andupdateactively reports the broken state as current. - The failure mode compounds: "Not used recently" reads as the model isn't choosing to use this plugin, not the plugin is broken, so users deprioritize rather than debug it.
Suggested fixes (any one would do)
plugin updateshould compare content (marketplace entry hash / commit SHA of the plugin subtree) rather than the version string alone — mirroring what #41922 fixed forinstall.- Marketplace policy: bump plugin versions on any manifest-affecting change (the format migration shipped behavior-relevant changes under an unchanged
1.0.0). - Startup validation: a plugin whose category/marketplace entry declares
lspServersbut whose materialized artifact registers zero LSP servers should surface in/pluginErrors instead of loading silently as an empty shell.
4 Comments
Confirming this reproduces on Claude Code 2.1.236, macOS (Darwin 25.6.0), with the JetBrains plugin (PyCharm) v0.1.14-beta also connected.
Both
pyright-lsp@claude-plugins-officialandtypescript-lsp@claude-plugins-official(v1.0.0, scope: user) showed as installed/enabled with prerequisites (pyright,pyright-langserver,typescript-language-server) present on PATH, yet no LSP tools were ever exposed to the model —claude mcp listreported no servers, and the cached.claude-plugin/plugin.jsonfor each plugin had onlyname/description/version/author, nolspServerskey.The documented workaround fixed it:
claude plugin uninstall <plugin>@claude-plugins-officialfollowed byclaude plugin install <plugin>@claude-plugins-officialfor both plugins. After reinstall,claude plugin detailsnow reports:vs. previously showing no LSP servers at all. Restarting the session should be sufficient to pick up the tool — will confirm. This is a strong +1 that the auto-repair path (
plugin update) needs to actually detect and fix this stale-shell state rather than reporting "already at the latest version."Reproduced, thank you for the excellent diagnosis — all three parts of your report check out on the current release (2.1.233), and this isn't macOS-specific (I reproduced it on Linux).
With a stale plugin artifact in the state you describe (a cached
plugin.jsongenerated before the manifest-format change, containing only name/description/version/author and no LSP server declaration):/pluginerrors. Debug logging showsTotal LSP servers loaded: 0versus1for a freshly-installed copy of the same plugin at the same version. The stale artifact's own manifest takes precedence over the marketplace's current declaration, which is why the break is invisible.plugin updateno-repair confirmed —claude plugin update pyright-lsp@claude-plugins-officialreportsalready at the latest version (1.0.0)and leaves the broken artifact byte-for-byte untouched, because the comparison is on the version string alone and the version never changed across the format migration.A recent release did fix a related
updatecase (stale reporting when the marketplace had newer commits), but the variant here — same version string, stale artifact content — is still present, which matches your experience.Until a fix lands, the workaround stands:
claude plugin uninstall+claude plugin installfor any LSP plugin installed before the manifest change, then restart. Marking this as reproduced.🤖 Generated with Claude Code
Additional data point: on the desktop app this isn't only a stale artifact from before the manifest-format change. The desktop app still produces it, so uninstall + install doesn't stick there.
Environment: Claude desktop 2.16120.0 (bundled Claude Code 2.1.284), macOS 27,
claude-plugins-officialLSP plugins at 1.0.0..claude-plugin/plugin.jsoninto the cache ofstrict: falsemarketplace plugins ([marketplaceFileOps] Synthesized plugin.json for <name> (strict:false marketplace plugin)). The synthesized manifest carriesname,description,versionandauthor(its whitelist includesmcpServers) but notlspServers. It is created on install/update when missing and overwritten on a desktop marketplace refresh.lspServersdon't apply when aplugin.jsonis present). Every session logsTotal LSP servers loaded: 0and theLSPtool is never registered.*-lspplugins within twelve seconds. Each one was an exact projection of its marketplace entry minuscategory,source,strictandlspServers.claude plugin detailsstill reportsLSP servers (1)from the marketplace entry, so the failure looks healthy.Workaround that survives the re-synthesis: put the server config into
.lsp.jsonat the cached plugin root, e.g.~/.claude/plugins/cache/claude-plugins-official/swift-lsp/1.0.0/.lsp.json:The synthesizer never writes
.lsp.json, and the loader reads it regardless ofplugin.json. Verified withclaude -p … --debug-file <file>: oneLoaded 1 LSP server(s) from plugin: …line per plugin,Total LSP servers loaded: 8, andLSPin the init event'stools.Suggested fix: include
lspServers(anduserConfig/channels) in the synthesized manifest, or skip synthesizing aplugin.jsonforstrict: falseentries, since the upstream plugin directories intentionally ship without one.Related: #90114, #90926, #15148, #93474.
Another consequence of the same synthesis, for entries that declare
skillsinstead oflspServers: withstrict: falsethe synthesized manifest makes the whole plugin fail to load (Plugin <name> has conflicting manifests), as documented in plugins-reference. Seen withacademy-guide,claude-apianddiscernment-nudgefromanthropic-agent-skillson Claude Desktop 2.16120.0, Windows 11. There were six marketplace refreshes between 2026-09-12 and 2026-10-01; each one synthesized the three manifests, and after each one the plugins were missing from the skill listing.Reading the 2.16120.0 bundle confirms the "overwritten on a desktop marketplace refresh" part.
ensureManifestsForMarketplacecallsensurePluginManifestExists(pluginId, …, true)for every installed plugin of the refreshed marketplace, and the writer has nostrictcheck. Install and update call it withoverwrite: false. On refresh, the only skip is a real.claude-plugin/plugin.jsonin the plugin's source directory.Catalog-side report: anthropics/skills#1943.