LSP diagnostics leak across sibling git worktrees
Summary
When two Claude Code sessions share a repository root via sibling git worktrees, an Edit tool call in one session causes the other session's LSP layer to emit <new-diagnostics> for the edited file — but rendered as if the file had no tsconfig context. Every global is reported as missing (Cannot find name 'Promise', Cannot find name 'Set', Cannot find name 'Record'), every path alias is unresolvable (Cannot find module '@/...'), and every built-in method is missing from its type (Property 'indexOf' does not exist on type 'string', Property 'push' does not exist on type 'string[]'). The project's tsc --noEmit passes cleanly; the errors are purely an LSP-surface artifact that streams into unrelated sessions.
Environment
- Claude Code version: latest as of 2026-04-17
- OS: Linux
- Project: Next.js 16 + TypeScript (repro reproducible on any TS project using
"paths": { "@/*": ["./src/*"] })
Steps to Reproduce
- In a repo with two git worktrees (main tree at
$REPOand a sibling at$REPO/.worktrees/<branch>/), start two Claude Code sessions:
- Session A:
cd $REPO && claude(main tree) - Session B:
cd $REPO/.worktrees/<branch> && claude(sibling worktree)
- In Session B, run any skill or direct prompt that triggers
EditorWritetool calls on.tsx/.tsfiles undersrc/. - Observe Session A's tool-result stream:
<new-diagnostics>blocks appear for files inside.worktrees/<branch>/src/...— files Session A never opened.
Expected Behavior
Diagnostics emitted by Session A's LSP surface should be scoped to Session A's CWD-rooted project (the main tree at $REPO), not to sibling worktrees that are independent TypeScript projects with their own tsconfig and node_modules.
Actual Behavior
Session A receives <new-diagnostics> blocks for files edited in Session B's worktree. Every diagnostic is symptomatic of TypeScript running without a lib AND without the project's path aliases. Representative samples:
activity-log.ts
✘ Cannot find name 'Promise'
✘ Cannot find name 'Set'
✘ Cannot find name 'Record'
✘ Property 'push' does not exist on type 'SQL<unknown>[]'
TaskDetailModal.tsx
✘ Cannot find module '@/types'
✘ Cannot find module '@/hooks'
✘ Cannot find name 'Promise'
Root Cause Analysis
Three pieces of evidence, taken together:
- The tsconfigs in main and worktree are byte-identical (
cmpreturns equal). Both declare"lib": ["dom", "dom.iterable", "esnext"]and"paths": { "@/*": ["./src/*"] }. - The worktree has its own fully-installed
node_moduleswithlib.es2015.d.ts,typescript/lib/tsc.js, etc. - Running
tsc --noEmitinside the worktree produces no errors; runningnpx tsc --noEmit --listFilesin the main tree returns zero files matching.worktrees/— the main-treetscdoes not walk into sibling worktrees.
So: neither tsc instance reproduces the error. The errors come from Claude Code's LSP integration, which appears to:
- Watch files across the entire CWD tree from the main session's root,
- Open each file in an isolated per-file language context when a sibling process modifies it,
- Fail to associate that per-file context with any tsconfig — producing the "no lib, no paths" symptom pattern.
The "no lib" signature is decisive: a tsconfig misconfiguration would lose @/* aliases or a specific lib entry, not every global at once. Only a file opened with no project context loses Promise, Set, Record, built-in methods on strings/arrays, and aliases simultaneously.
What I Tried
Two workarounds were investigated and ruled out based on the documented Claude Code surface:
- Settings-level LSP scope knob — the Claude Code settings reference (https://code.claude.com/docs/en/settings.md, ~225 documented keys) has zero LSP / diagnostics scope configuration keys. No
lsp.*,diagnostics.*,ignorePatterns,workspaceFolders, or equivalent. - PostToolUse hook that strips diagnostic entries — the hooks reference (https://code.claude.com/docs/en/hooks.md) documents
updatedMCPToolOutputas MCP-only. PostToolUse hooks can block or add context, but cannot mutate built-in tool result payloads.
A third workaround (excluding .worktrees/ from the project's tsconfig.json exclude array) is being landed downstream as a belt-and-braces hint for any LSP implementation that consults tsconfig discovery — but since the observed behavior suggests files are opened without a discovered tsconfig, this does not reliably stop the leak.
Requested Fix
A policy in Claude Code's LSP integration to scope file watching and diagnostic emission to the session's CLAUDE_PROJECT_DIR (or the git-worktree boundary, equivalently), so sibling worktrees do not leak <new-diagnostics> into unrelated sessions. Either:
- Ideal: LSP watcher treats
CLAUDE_PROJECT_DIRas the project root and ignores any file whose absolute path is outside it. - Acceptable workaround: a
settings.jsonkey (e.g.,lsp.ignorePaths: [".worktrees/**"]orlsp.projectRoot) that lets projects opt out of the watcher for specific subtrees.
Operational Impact
Running 3+ parallel Claude Code sessions from a single repo-root (a common pattern for concurrent feature work across worktrees) multiplies the cross-session noise. Every sibling Edit sends spurious diagnostics to every peer session, wasting tokens, polluting context, and creating false urgency. The bug does not cause incorrect output — the LSP is wrong, the project compiles fine — but the noise is significant enough that a downstream project is shipping the tsconfig workaround in production.
Related Internal Tracking
- RailVio task: SDLC-1011 (private tracker; happy to share redacted details on request)
- Downstream
tsconfigworkaround PR: will link once merged
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗