[BUG] LSP output displays stale diagnostics after out-of-band file writes (Bash, PostToolUse)

Status Open
Reported on v2.1.111
Maintainer reply None cached
Activity 1 comment · opened Jul 22, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

#17979 and #24443 tracked stale LSP diagnostics after Edit / Write tool calls.

This issue was fixed in Claude Code version 2.1.111, but the same issue still exists as of 2.1.217 for Bash tool calls as well as PostToolUse hooks (e.g. prettier reformatting hooks) which modify files.

Because Claude Code likes to use sed, python, and similar tools to edit files in place, these changes must be pushed to the LSP server explicitly, or else any diagnostics will remain stale or missing until the next Edit or Write tool call.

What Should Happen?

Claude Code should not output stale LSP diagnostics after out-of-band file writes with Bash tool calls.

Error Messages/Logs

Steps to Reproduce

Initialization
npm install -g typescript-language-server
mkdir /tmp/lsp-repro-dot-claude && mkdir /tmp/lsp-repro
printf '{\n  "compilerOptions": { "strict": true, "noEmit": true }\n}\n' > /tmp/lsp-repro/tsconfig.json
cd /tmp/lsp-repro

# Note: TS7 is the current default, but it is not compatible with typescript-language-server, so this repro uses TS6.
npm init -y && npm install -D typescript@6 prettier
printf '{ "printWidth": 120, "singleQuote": true }\n' > .prettierrc

cat > /tmp/lsp-repro-dot-claude/settings.json <<'JSON'
{
  "hooks": {
    "PostToolUse": [
      { "matcher": "Write|Edit",
        "hooks": [ { "type": "command",
          "command": "jq -r '.tool_response.filePath // .tool_input.file_path' | { read -r f; npx prettier --write \"$f\"; } 2>/dev/null || true" } ] }
    ]
  }
}
JSON

ENABLE_LSP_TOOL=1 CLAUDE_CONFIG_DIR=/tmp/lsp-repro-dot-claude claude

# log in to claude, then inside claude:

> /plugin install typescript-lsp@claude-plugins-official
> /exit

ENABLE_LSP_TOOL=1 CLAUDE_CONFIG_DIR=/tmp/lsp-repro-dot-claude claude
Repro Bash failure
Use the Write tool to write to a.ts the line export const x: number = 1;. Report any <new-diagnostics>, if any.

⏺ Write(a.ts)
Wrote 1 line to a.ts
export const x: number = 1;

Wrote a.ts with export const x: number = 1;. No <new-diagnostics> were reported.

❯ Use the Edit tool to change a.ts so 1 is "foo". Report any <new-diagnostics>, if any.

⏺ Update(a.ts)
Added 1 line, removed 1 line
export const x: number = 1;
export const x: number = "foo";

No <new-diagnostics> were surfaced automatically by the edit. Let me query the LSP directly to confirm, since this change (number = "foo") should produce a type error.
Found 1 new diagnostic issue in 1 file (ctrl+o to expand)

The edit produced a new diagnostic, now surfaced:

a.ts

  • ✘ [Line 1:14] Type 'string' is not assignable to type 'number'. — [2322] (typescript)

This is expected: x is declared as number but assigned "foo".

❯ Use the Edit tool to change a.ts so "foo" is 42. Report any <new-diagnostics>, if any.

⏺ Update(a.ts)
Added 1 line, removed 1 line
export const x: number = "foo";
export const x: number = 42;

No <new-diagnostics> were reported. The type error is resolved — x: number = 42 is valid, so a.ts is clean.

❯ Use the Bash tool to invoke sed on a.ts to change 42 to "bar". Report any <new-diagnostics>, if any.

Ran 1 shell command

No <new-diagnostics> were surfaced this time. Unlike Write/Edit, edits made through Bash (like sed) don't trigger the harness's automatic diagnostic reporting — the LSP layer isn't notified of the change.

The edit itself did introduce a type error, though. The file now reads:

export const x: number = "bar";

That's the same string-not-assignable-to-number problem as before (error 2322), but because the change bypassed the tracked file tools, no <new-diagnostics> reminder was emitted. The available LSP tool doesn't expose a diagnostics/pull operation to fetch it on demand, so I can confirm the error by inspection but can't surface it as a formal <new-diagnostics> block.

Repro PostToolUse failure
❯ Use the Write tool to create bad.ts with exactly this one line:
  `export const cfg: Record<string, number> = { alpha: 1, beta: 2, gamma: 3, delta: 4, epsilon: 5, zeta: 6, eta: 7, theta: 'not a number', iota: 9, kappa: 10 };`
  Then report any <new-diagnostics>.
⏺ Write(bad.ts)
  ⎿  Wrote 1 line to bad.ts
      1 export const cfg: Record<string, number> = { alpha: 1, beta: 2, gamma: 3, delta: 4, epsilon: 5, zeta: 6, eta: 7, theta: 'not a number', iota: 9, kappa: 10 };
  ⎿  Found 1 new diagnostic issue in 1 file (ctrl+o to expand)

⏺ Done. The file was created, and the type checker reported one new diagnostic:

  bad.ts
  - ✘ Line 1, col 114 — Type 'string' is not assignable to type 'number'. [2322]

  This is the theta: 'not a number' entry — a string value in a Record<string, number>. All other entries are valid numbers.

Note here it claims the error is on Line 1, but with the PostToolUse hook, it was reformatted already onto line 9:

cat /tmp/lsp-repro/bad2.ts
export const cfg: Record<string, number> = {
  alpha: 1,
  beta: 2,
  gamma: 3,
  delta: 4,
  epsilon: 5,
  zeta: 6,
  eta: 7,
  theta: "not a number",
  iota: 9,
  kappa: 10,
};

Claude Model

Opus

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.217

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Environment

  • Claude Code: 2.1.217
  • OS: Darwin 25.5.0 (macOS)
  • Node: v26.1.0
  • LSP: typescript-lsp@claude-plugins-official, ENABLE_LSP_TOOL=1
  • typescript-language-server 5.3.0, typescript 6.0.3

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗