JetBrains plugin: DiagnosticTools listener fires on invalidated PSI from background project windows (regression of #37543)
Summary
The Claude Code JetBrains plugin's DiagnosticTools daemon listener accesses invalidated PSI elements from project windows other than the one whose daemon emitted the event. This produces SEVERE PluginExceptions in the IDE log, contributes to memory pressure, and was previously reported in #37543 (closed as stale by the bot without a fix). The suggested fix in that issue (a psiFile.isValid guard) does not appear to have been applied in plugin v0.1.14-beta.
Environment
- IDE: PhpStorm 2026.1.2 (Build PS-261.24374.185)
- Plugin: Claude Code [Beta] v0.1.14-beta (
com.anthropic.code.plugin) - JDK: OpenJDK 25.0.2 (JetBrains)
- OS: macOS (Darwin 25.5.0)
- Setup: Three git worktrees of the same monorepo open as three separate PhpStorm projects simultaneously.
Reproduction
- Open three (or more) project windows of related projects in PhpStorm simultaneously — e.g., git worktrees of the same repo.
- Edit a file in one window so the PSI for that path becomes invalidated.
- Trigger a daemon-finished event in any of the windows (regular editing or background re-analysis is enough; no Cmd+\` needed, unlike #37543).
Observed
SEVERE exceptions in idea.log, originating in DiagnosticTools.kt:106:
SEVERE - #c.i.u.m.i.MessageBusImpl - Invalid PSI Element: class com.jetbrains.php.lang.psi.PhpFileImpl #PHP
because: file:///…/monorepo-jv-express-pack/backend/src/…/ForceCompleteOrderStatusValidator.php (invalid) is invalid
com.intellij.diagnostic.PluginException: Invalid PSI Element …
at com.intellij.psi.util.PsiUtilCore.ensureValid(PsiUtilCore.java:526)
at com.intellij.psi.impl.source.PsiFileImpl.getTextLength(PsiFileImpl.java:405)
at com.intellij.psi.impl.source.PsiFileImpl.getTextRange(PsiFileImpl.java:411)
at com.intellij.codeInsight.daemon.impl.FileStatusMap.getFileDirtyScope(FileStatusMap.java:241)
at com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl.isErrorAnalyzingFinished(DaemonCodeAnalyzerImpl.java:713)
at com.anthropic.code.plugin.mcp.tools.DiagnosticTools\$addTools\$1\$1\$1.daemonFinished(DiagnosticTools.kt:106)
at com.intellij.codeInsight.daemon.DaemonCodeAnalyzer\$DaemonListener.daemonFinished(DaemonCodeAnalyzer.java:123)
Counts in my recent two log files:
idea.log(current session): 8 occurrencesidea.1.log(previous session): 87 occurrences
The invalidated PSI in the stack trace belongs to a project other than the currently focused window, indicating the listener is registered at application scope and doesn't filter or validate before calling isErrorAnalyzingFinished().
Impact
- Repeated
SEVERElog spam (95 occurrences across two recent sessions). - Plugin holds stale PSI references → contributes to memory pressure. In my session the IDE also emitted
Low memory signal receivedand ran into a separate PHP Stub index corruption (SerializedStubTree.getStubIndicesValueMap must not return null) that triggered an app-wideRebuild requested for index Stubs, causing all three open projects to reindex ~95k files each (~7 minutes of pegged CPU). I do not claim this plugin caused the stub corruption, but the listener bug worsens memory headroom for users who hit the JetBrains stub bug. - The
Plugin to blame: PHPblame attribution in the log is misleading — the bottom-of-stack frame is from this plugin, not the PHP plugin.
Suggested fix
Per #37543, guard the PSI access in DiagnosticTools.kt:106:
\\\kotlin\
if (psiFile.isValid) {
daemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)
}
\\
Or filter the listener by project at registration so it only sees events from the project it was scoped to.
References
- Closed-as-stale predecessor: #37543
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗