[BUG] LSP diagnostics are never awaited after Edit/Write (the race condition described in #17979 is still present)

Status Open
Reported on v2.1.111
Maintainer reply None cached
Activity 1 comment · opened Sep 10, 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?

The underlying race condition described in #17979 is still present: Claude Code does not await the LSP ‎textDocument/publishDiagnostics push before finalizing and returning an Edit/Write tool result. Because ‎publishDiagnostics is an asynchronous, server-initiated notification, the diagnostics for a file the model just edited arrive after that edit’s tool result has already been sealed, and get flushed onto a later tool result or the next user turn.

The fix that shipped in 2.1.111 (“Fixed LSP diagnostics from before an edit appearing after it, causing the model to re-read files it just edited”), which closed #17979 as completed, does not fix the root cause. It appears to only filter out the just-edited file’s diagnostics from the immediately-following tool result to suppress the visibly-stale ones. The diagnostics are still not awaited, which cause them to always appear on a next turn's result which is confusing for the LLM since he get them with the next turn's request which is maybe an out-of-context prompt or tool-call result.

Concretely:

  1. I ask the model to introduce an error into a file.
  1. No diagnostics are returned alongside that Edit’s tool result.
  1. I then send another unrelated message. Now the edited file’s diagnostics arrive

I reproduced it with ‎lua-lsp (lua-language-server) and ‎pyright-lsp (pyright-langserver). But this issue probably appear for any LSP servers.

This is an interactive session (not ‎claude -p).

What Should Happen?

The documentation promises same-turn behavior. From Discover plugins → Code intelligence ↗ (https://code.claude.com/docs/en/discover-plugins#code-intelligence):

“after every file edit Claude makes, the language server reports errors and warnings back… If Claude introduces an error, it notices and fixes it in the same turn.”

For that to hold, after an Edit/Write, Claude Code should await the server’s ‎publishDiagnostics for the edited document (bounded by a short, version-matched timeout) and attach the current diagnostics to that same tool result.

Error Messages/Logs

No error is thrown; the failure is silent/behavioral. The visible signal is timing: after the first edit, the ‎`Found N new diagnostic issues in M files` indicator does not appear on that turn, and the edited file’s diagnostics only surface on a subsequent tool result / user turn.

Here are my logs after an Edit tool call on a lua file in my repo (handlers.lua in this example).
2026-09-10T08:41:27.113Z [DEBUG] [API REQUEST] /api/v1/messages source=repl_main_thread
2026-09-10T08:41:28.161Z [DEBUG] Stream started - received first chunk
2026-09-10T08:41:28.161Z [DEBUG] [API:timing] first byte after 1049ms
2026-09-10T08:41:58.862Z [INFO] [Stall] tool_dispatch_start tool=Edit toolUseId=call_a21cf6965a8d4809b8de1171 permissionDecisionMs=29241
2026-09-10T08:41:58.883Z [DEBUG] Writing to temp file: /Users/julien/.config/nvim/lua/user/lsp/handlers.lua.tmp.92691.ba8e5ad2c8a3
2026-09-10T08:41:58.883Z [DEBUG] Temp file written successfully, size: 4703 bytes
2026-09-10T08:41:58.883Z [DEBUG] Renaming /Users/julien/.config/nvim/lua/user/lsp/handlers.lua.tmp.92691.ba8e5ad2c8a3 to /Users/julien/.config/nvim/lua/user/lsp/handlers.lua
2026-09-10T08:41:58.884Z [DEBUG] File /Users/julien/.config/nvim/lua/user/lsp/handlers.lua written atomically
2026-09-10T08:41:58.884Z [DEBUG] LSP Diagnostics: Clearing delivered diagnostics for /Users/julien/.config/nvim/lua/user/lsp/handlers.lua
2026-09-10T08:41:58.884Z [DEBUG] [LSP PROTOCOL plugin:lua-ls:lua] Sending notification 'textDocument/didChange'.
2026-09-10T08:41:58.884Z [DEBUG] [LSP PROTOCOL plugin:lua-ls:lua] Sending notification 'textDocument/didSave'.
2026-09-10T08:41:58.884Z [INFO] [Stall] tool_dispatch_end tool=Edit toolUseId=call_a21cf6965a8d4809b8de1171 outcome=ok durationMs=22
2026-09-10T08:41:58.887Z [DEBUG] LSP Diagnostics: getLSPDiagnosticAttachments called
2026-09-10T08:41:58.887Z [DEBUG] LSP Diagnostics: Checking registry - 0 pending
2026-09-10T08:41:58.887Z [DEBUG] Hooks: Found 0 total hooks in registry
2026-09-10T08:41:58.887Z [DEBUG] Hooks: checkForNewResponses returning 0 responses
2026-09-10T08:41:58.892Z [DEBUG] LSP: Sent didChange for /Users/julien/.config/nvim/lua/user/lsp/handlers.lua (v2)
2026-09-10T08:41:58.892Z [DEBUG] LSP: Sent didSave for /Users/julien/.config/nvim/lua/user/lsp/handlers.lua
2026-09-10T08:41:58.911Z [DEBUG] [API REQUEST] /api/v1/messages source=repl_main_thread
2026-09-10T08:41:59.176Z [DEBUG] [LSP PROTOCOL plugin:lua-ls:lua] Received notification 'textDocument/publishDiagnostics'.
2026-09-10T08:41:59.176Z [DEBUG] [PASSIVE DIAGNOSTICS] Handler invoked for plugin:lua-ls:lua! Params type: object
2026-09-10T08:41:59.176Z [DEBUG] Received diagnostics from plugin:lua-ls:lua: 11 diagnostic(s) for file:///Users/julien/.config/nvim/lua/user/lsp/handlers.lua
2026-09-10T08:41:59.176Z [DEBUG] LSP Diagnostics: Registering 1 diagnostic file(s) from plugin:lua-ls:lua (ID: ceb70634-60be-4f4c-aef9-3bec4e075630)
2026-09-10T08:41:59.176Z [DEBUG] LSP Diagnostics: Registered 1 diagnostic file(s) from plugin:lua-ls:lua for async delivery
2026-09-10T08:41:59.779Z [DEBUG] Stream started - received first chunk
2026-09-10T08:41:59.779Z [DEBUG] [API:timing] first byte after 869ms
2026-09-10T08:42:00.096Z [DEBUG] [engine] turn 3 end (turns=2 usage in=16020 out=593 cost=$0.1319 api=14617ms stop=end_turn resultLen=58)

This demonstrate that the 'getLSPDiagnosticAttachments' is called 3ms (8:41:58.887) after the file write (8:41:58.884). (3ms is not enough for the LSP to report the diagnostics, so this return 0 diagnostics. The [API REQUEST] that send the tool call results back to the model is done at 8:41:58.911 (27ms after the file write). The LSP server then send the 'textDocument/publishDiagnostic' at 8:41:59:176 (292ms after the file write). Awaiting the publishDiagnostic for maybe 1sec would probably be enough for most cases (in my example 0.3sec is enough). Ideally this timing should be configurable by the user depending on the need, and also maybe be an opt-in functionality so that we make sure to not impact users who don't want to way 1sec extra after each edit call.

Steps to Reproduce

  1. Install the language server binary: ‎lua-language-server must be on ‎$PATH.
  1. In an interactive Claude Code session, install the plugin: ‎/plugin install lua-lsp@claude-plugins-official (run ‎/reload-plugins if prompted).
  1. Ask the model: “Please edit this file to introduce a bug (e.g. call an undefined function).”
  1. Observe: the Edit tool result is returned with no diagnostics — the ‎Found N new diagnostic issues indicator does not appear on this turn.
  1. Send a trivial follow-up message, e.g. “ok thank you” (no edit, no file-related request).
  1. Observe: the diagnostics for the edited file are now returned on this new turn, even though nothing in this turn touched the file. This shows the diagnostics from the previous turn’s edit were not awaited and are delivered late.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.267

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

  • Related issues: #17979 (original bug; the “fix” that this report shows is incomplete), #24443 (clean Edit/Write race-condition repro), #64238 (decouple diagnostic auto-push — related rework).
  • Root cause is the async ‎publishDiagnostics push not being awaited before the Edit/Write tool result is finalized.
  • Session type: interactive (not ‎-p).
  • Plugins tested: ‎lua-lsp, ‎pyright-lsp (both from ‎claude-plugins-official). (but probably all the LSP plugins have the same behavior)

View original on GitHub ↗

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