LSP tool silently drops goToDefinition/findReferences results for gitignored paths — breaking navigation inside .claude/worktrees/ (its own worktree feature)

Open 💬 0 comments Opened Jul 10, 2026 by K-dash

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:

  1. LSP hover on target_func at line 5 → works, returns the signature
  2. LSP goToDefinition on the same position → "No definition found." — although the definition is in the same file, four lines up
  3. LSP findReferences on 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: goToDefinition into any gitignored directory is silently suppressed — e.g. jumping into dependency sources under .venv/ (Python) or node_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)

  1. Run check-ignore in 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.
  2. Exempt **/.claude/worktrees/ — Claude Code's runtime exclusion (meant to keep git status clean) should not suppress LSP navigation results in its own feature.
  3. Skip the filter for goToDefinition/goToImplementation entirely; ignore-filtering makes sense for noise reduction in findReferences/workspaceSymbol, but definitions legitimately live in gitignored dependency directories.
  4. 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.

View original on GitHub ↗