LSP tool silently drops goToDefinition/findReferences results for gitignored paths — breaking navigation inside .claude/worktrees/ (its own worktree feature)
Environment
- Claude Code v2.1.206 (macOS arm64, native binary)
- Reproduced with a third-party LSP proxy (typemux-cc → pyright), but the filtering is client-side and independent of the LSP server used
Summary
The LSP tool post-filters URI-bearing results (goToDefinition, findReferences, goToImplementation, workspaceSymbol) through git check-ignore executed in the session's cwd, and silently drops every location whose path is ignored in that git context. Because Claude Code itself registers **/.claude/worktrees/ in .git/info/exclude (the # claude-code-runtime block), all definition/reference results pointing inside its own worktrees are dropped whenever the session runs at the main repository root. The tool then reports "No definition found", with a hint text that misdirects the user toward LSP server indexing issues.
hover and documentSymbol results carry no URIs, bypass the filter, and work for the same files — which makes the failure look like an LSP server bug rather than client-side filtering.
Reproduction
cd some-repo-with-lsp-enabled # session cwd = main repo root
git worktree add --detach .claude/worktrees/probe
mkdir -p .claude/worktrees/probe/src
cat > .claude/worktrees/probe/src/probe.py << 'EOF'
def target_func(name: str) -> str:
return f"hello {name}"
result = target_func("world")
EOF
From a Claude Code session running at the repo root:
LSP hoverontarget_funcat line 5 → works, returns the signatureLSP goToDefinitionon the same position → "No definition found." — although the definition is in the same file, four lines upLSP findReferenceson line 1 → "No references found."
Mechanism check outside Claude Code:
$ git check-ignore -v /abs/path/to/repo/.claude/worktrees/probe/src/probe.py
.git/info/exclude:11:**/.claude/worktrees/ /abs/path/to/repo/.claude/worktrees/probe/src/probe.py
# → matched: every LSP location under the worktree is filtered out
$ git -C .claude/worktrees/probe check-ignore -v /abs/path/to/repo/.claude/worktrees/probe/src/probe.py
# → exit 1 (not ignored): from inside the worktree the same path is fine
The second command explains the known workaround: launching Claude Code inside the worktree makes navigation work, because paths then resolve relative to the worktree's toplevel and no longer match **/.claude/worktrees/.
Control experiment: an identical worktree placed in a non-ignored directory (e.g. .worktree/probe2 with no matching ignore rule) — goToDefinition works from the main repo root. The single discriminating variable is the gitignore status of the worktree path.
Root cause (from the bundled JS of v2.1.206)
In the LSP tool's call(), array results for goToDefinition/findReferences/goToImplementation/workspaceSymbol are passed through a helper that batches the location paths into git check-ignore (chunks of 50, cwd = session cwd, 5s timeout) and drops every reported path. The result formatter then prints "No definition found. This may occur if the cursor is not on a symbol, or if the definition is in an external library not indexed by the LSP server." — attributing the empty result to the LSP server.
Impact
- Code navigation is broken inside worktrees created by Claude Code's own worktree feature whenever the session cwd is the main repo root.
- Beyond worktrees:
goToDefinitioninto any gitignored directory is silently suppressed — e.g. jumping into dependency sources under.venv/(Python) ornode_modules/(JS), which is a primary use of goToDefinition. - The hint text actively misdirects debugging toward the LSP server ("not indexed"), when the locations were returned correctly and dropped client-side.
Suggested fixes (any of)
- Run
check-ignorein the git context of each result path (e.g.git -C <dir(path)> check-ignore) instead of the session cwd, so worktree-internal paths are evaluated in their own worktree. - Exempt
**/.claude/worktrees/— Claude Code's runtime exclusion (meant to keepgit statusclean) should not suppress LSP navigation results in its own feature. - Skip the filter for
goToDefinition/goToImplementationentirely; ignore-filtering makes sense for noise reduction infindReferences/workspaceSymbol, but definitions legitimately live in gitignored dependency directories. - At minimum, stop reporting a filtered-to-empty result as "No definition found" — say "N result(s) hidden (gitignored paths)" so users can tell filtering from a genuine miss.