Stale TypeScript diagnostics after Edit/Write — race condition with TS language server
Problem
After Edit or Write tool calls, the <new-diagnostics> tags frequently contain stale TypeScript errors from the pre-edit file state. This is because the TS language server (tsserver) hasn't finished re-analyzing the file by the time Claude Code collects diagnostics.
This causes Claude to:
- "Fix" errors that don't actually exist
- Waste turns investigating phantom type errors
- Sometimes introduce new bugs by reverting correct changes
Reproduction
Rapid sequential edits on a complex TypeScript file reliably trigger this:
- Edit 1: Introduce a type error (e.g., change
private readonly DEFAULT_CHUNK_SIZE = 100to= 'not-a-number')
- Result: No diagnostics reported (tsserver hasn't caught up yet)
- Edit 2: Fix it back to
= 100
- Result: Stale diagnostics appear from the broken intermediate state (e.g.,
Operator '+=' cannot be applied to types 'number' and 'string | number')
- Running
npx tsc --noEmitconfirms zero actual errors — the diagnostics were phantom
Confirmed across 29 incidents in 17 sessions in a single project.
Root Cause
Classic race condition:
Edit/Write completes → Claude Code reads diagnostics → tsserver finishes re-analyzing
↑ stale data here ↑ fresh data arrives too late
The TS language server uses file watchers to detect changes, then re-parses, resolves imports, and type-checks. For complex files with deep import chains, this takes 200–800ms. Claude Code collects diagnostics immediately after the edit tool returns, before tsserver completes.
Workaround (confirmed working)
A PostToolUse hook that adds a 1-second delay after Edit/Write, giving tsserver time to catch up:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "sleep 1",
"statusMessage": "Letting TS language server catch up to prevent stale diagnostics"
}
]
}
]
}
}
Before hook: Edit 2 (fix) reports stale errors from Edit 1 (break)
After hook: Edit 2 (fix) correctly reports zero errors
Suggested Platform Fix
Rather than requiring users to add a sleep hook, Claude Code could:
- Wait for tsserver to settle before collecting diagnostics (e.g., wait for the
semanticDiagresponse after the file change event) - Debounce diagnostic collection — if an edit just happened, delay collection by ~1s
- Mark diagnostics as potentially stale when they're collected within N ms of an Edit/Write, so the model knows not to trust them blindly
- Skip diagnostic collection entirely after Edit/Write and let the model explicitly request them if needed
Option 1 is the most correct. Option 3 is the lowest-effort improvement.
Environment
- Claude Code (CLI)
- TypeScript project with ~60 source files
- macOS (Darwin 25.2.0)
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗