[LSP] Language servers on user PATH not found — "Command 'X' not found or is in an unsafe location (current directory)" (rust-analyzer + clangd, Windows)

Status Open
Maintainer reply None cached
Activity 6 comments · opened Jul 8, 2026

Summary

The Claude Code LSP plugin host fails to launch any language server whose binary lives on the user PATH (rather than the machine/system PATH), erroring with:

Command 'rust-analyzer' not found or is in an unsafe location (current directory)

The identical failure happens with clangd. Both binaries are correctly installed, on the user's PATH, and run fine from a shell. LSP is therefore completely unusable for Rust and C/C++ on this setup.

Because rust-analyzer-lsp and clangd fail the same way, this is the shared LSP host / server-resolution logic, not a per-plugin bug.

Environment

  • OS: Windows 11
  • Claude Code: desktop app
  • Plugins (claude-plugins-official): rust-analyzer-lsp v1.0.0, clangd
  • rust-analyzer 1.96.1 — %USERPROFILE%\.cargo\bin\rust-analyzer.exe (rustup proxy). rust-analyzer --version works in a shell.
  • clangd%LOCALAPPDATA%\Programs\clangd\22.1.6\bin\clangd.exe, on the user PATH. clangd --version works in a shell.

Reproduction

  1. rustup component add rust-analyzer (proxy lands in ~/.cargo/bin, which is on the user PATH).
  2. Open a Rust project; invoke any LSP operation (e.g. workspaceSymbol).
  3. Command 'rust-analyzer' not found or is in an unsafe location (current directory).
  4. Same on a C/C++ file → Command 'clangd' not found or is in an unsafe location (current directory).

What was ruled out (extensively)

  • Binary is fine: rust-analyzer --version / clangd --version both run from a shell on the same machine/user.
  • Not the rustup symlink shim: replacing ~/.cargo/bin/rust-analyzer.exe (symlink → rustup.exe) with a real copy of the binary / a copy of rustup.exe — no change.
  • Not editor-specific: identical failure in both VS Code and Zed.
  • Not the PATH location: placing a working proxy in %LOCALAPPDATA%\Microsoft\WindowsApps (user PATH) and in C:\Windows\System32 (system PATH — on every process's PATH), followed by a full app restart, still fails identically.
  • Affects both plugins the same way → shared host, not per-plugin.

Likely cause

The LSP host process appears to run with a stripped environment that omits the user's PATH entries, and/or resolves the server binary once at host startup and caches the "not found" result (adding the binary to System32 afterward + restarting did not help). The "unsafe location (current directory)" guard may additionally be over-rejecting otherwise-valid resolutions.

Impact

Go-to-definition, find-references, diagnostics, and symbol search are entirely unavailable for Rust and C/C++, despite correctly-installed, PATH-available, working language servers. This is a poor first-run experience: users install exactly what the plugin READMEs instruct (rustup component add rust-analyzer, clangd on PATH), the binaries work everywhere else on the machine, yet the plugin never finds them — with no configuration knob to point it at the right path.

Suggested fixes

  1. Spawn the LSP host with the full user environment/PATH (not just the system PATH).
  2. Additionally probe common per-user install locations (~/.cargo/bin, %LOCALAPPDATA%\Programs\..., Homebrew, etc.).
  3. Provide an explicit command / server-binary-path override in plugin or user config.
  4. Re-resolve the server per request instead of caching a startup "not found".

View original on GitHub ↗

6 Comments

adithya-s-edstem · 1 month ago

^ Do not open the above zip got the same comment in my issue from another account.

r3bb1t · 1 month ago

yea, immediately got that zip right when posted an issue. Kinda sus

skoulik · 1 month ago

Ran into the same error with clangd-lsp on Windows 10 (Claude Code 2.1.204, native claude.exe build) and dug into the resolution logic. Findings that may help narrow it down:

PATH placement is irrelevant — confirmed. clangd 22.1.8 (winget LLVM.LLVM) failed identically from three different directories: a standalone dir on PATH, C:\Program Files\LLVM\bin (front of user PATH, where.exe clangd resolves it), and even the plugin's own cache bin dir. Fresh process each time, so it's not the cached negative result.

The resolver's algorithm looks correct — the failure seems runtime-specific. From the bundled JS in claude.exe (minified names from 2.1.204): commands containing a slash bypass resolution entirely (iSe); bare names go through REn, which runs %SYSTEMROOT%\System32\where.exe <cmd> via execFileSync with env: process.env, then filters each candidate:

  1. skip if lstatSync(c, {throwIfNoEntry:false}) === undefined (X9a)
  2. skip + flag unsafe if under process.cwd() (Umn) — notably, if fs.realpathSync.native() of the candidate's parent dir throws, the candidate is treated as unsafe
  3. skip unless extension ∈ {.com, .exe, .bat, .cmd} (xom)

I replicated this exact chain in Node 20 from the same environment and working directory: where.exe returns the candidates and every filter passes. Yet inside claude.exe the same inputs produce Command 'clangd' not found or is in an unsafe location (current directory). So the suspect is a behavioral difference in the native build's runtime — execFileSync of where.exe (options include timeout + windowsHide) or realpathSync.native are the candidates worth checking.

Workaround that works today: an absolute path in the LSP server config skips the resolver (the slash check above). Since the official plugin hardcodes "command": "clangd", this means either patching the marketplace cache entry (reverts on refresh) or a small local plugin whose lspServers uses the absolute path.

Second, separate bug found while testing the workaround: if the configured command is an absolute path containing spaces (e.g. C:\Program Files\LLVM\bin\clangd.exe), the LSP tool silently fails to register — no error anywhere, the tool just doesn't exist in the session. The 8.3 short form (C:\PROGRA~1\LLVM\bin\clangd.exe) works and gives fully functional LSP (cross-TU findReferences/goToDefinition verified against a CMake/Qt project with compile_commands.json). Happy to file that one separately if useful.

skoulik · 1 month ago

The above comment was authored and posted by claude itself, sorry for the slop, guys. But it is correct if you care to read through it.

skoulik · 1 month ago

As a workaround we just ended up writing an override plugins\clangd-local-override\.claude-plugin\plugin.json with the full path without spaces and removing the official spec.

Btw, clangd-lsp does not handle .m/.mm files in windows... Had to add the extensions to my override.

r3bb1t · 1 month ago

@skoulik thanks, it really helped