[BUG] [JetBrains] Claude Code plugin freezes IDE — DiffTools.applyDiff calls runReadAction on the EDT from a Swing actionPerformed handler
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 Claude Code [Beta] JetBrains plugin's DiffTools.applyDiff runs ApplicationImpl.runReadAction(...) synchronously on the AWT Event Dispatch Thread (EDT), invoked from a Swing AbstractButton.fireActionPerformed handler on the diff-viewer Accept button.
If any background reader is slow at that moment, the EDT parks waiting for the global read permit and the entire IDE becomes "Not Responding" until the reader finishes — clicks, scrolling, and keyboard input are all dropped. Recovery requires force-killing the IDE.
In my reproduction the read lock was held by the bundled Grazie / Proofreading inspector, which had entered catastrophic regex backtracking inside CheckerRunner.doRun while visiting a comment in the Go file I was editing (full stack in the attached dump shows ~270 frames of java.util.regex.Pattern$Curly.match / Branch.match recursion on a single worker). But Grazie is just the trigger I happened to hit — the root cause is the plugin's EDT-blocking call pattern. Any other slow read-action holder (indexing, a third-party inspection, a long-running annotator) would produce the same freeze.
What Should Happen?
The plugin should not block the EDT on runReadAction. The diff-apply flow should use a non-blocking read action so the UI stays responsive regardless of what other plugins/subsystems are doing under the read lock.
Suggested fix (rough sketch):
// Instead of synchronous:
// ApplicationManager.getApplication().runReadAction { ... }
// Option A: non-blocking read action
ReadAction.nonBlocking {
// PSI / VFS / document reads happen here, off-EDT
}
.expireWith(parentDisposable)
.finishOnUiThread(ModalityState.defaultModalityState()) { result ->
// EDT-side follow-up (write action, dialog dismissal, etc.)
}
.submit(AppExecutorUtil.getAppExecutorService())
// Option B: coroutine-based (since DiffTools.addTools is already a coroutine)
val result = readAction { /* off-EDT read */ }
withContext(Dispatchers.EDT) { /* UI update / write action */ }
Either way, the Swing actionPerformed handler should return immediately and the actual work should happen off the EDT.
Error Messages/Logs
EDT stack at freeze time (truncated; full dump attached):
"AWT-EventQueue-0" prio=0 tid=0x0 nid=0x0 waiting on condition
java.lang.Thread.State: TIMED_WAITING
at java.base@25.0.2/jdk.internal.misc.Unsafe.park(Native Method)
at java.base@25.0.2/java.util.concurrent.locks.LockSupport.parkNanos(...)
at java.base@25.0.2/java.util.concurrent.LinkedBlockingQueue.poll(...)
at com.intellij.openapi.progress.util.EventStealer.waitForPing(EventStealer.java:126)
at com.intellij.openapi.progress.util.SuvorovProgress.showNiceOverlay(SuvorovProgress.kt:216)
at com.intellij.openapi.progress.util.SuvorovProgress.dispatchEventsUntilComputationCompletes(SuvorovProgress.kt:153)
at com.intellij.openapi.application.impl.ApplicationImpl.lambda$postInit$15(ApplicationImpl.java:1453)
...
at com.intellij.platform.locking.impl.NestedLocksThreadingSupport.smartAcquireReadPermit(NestedLocksThreadingSupport.kt:801)
at com.intellij.platform.locking.impl.NestedLocksThreadingSupport.runReadAction(NestedLocksThreadingSupport.kt:845)
at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:1099)
at com.anthropic.code.plugin.mcp.tools.DiffTools$addTools$1
.invokeSuspend$lambda$12$applyDiff(DiffTools.kt:138)
at com.anthropic.code.plugin.mcp.tools.DiffTools$addTools$1
.invokeSuspend$lambda$12$lambda$6$lambda$5(DiffTools.kt:228)
at com.anthropic.code.plugin.mcp.tools.DiffTools$addTools$1
$$Lambda/0x0000007c05042000.actionPerformed(Unknown Source)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1972)
at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2314)
at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:407)
at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:279)
... (regular AWT mouse-event dispatch above)
Key plugin frames:
- `DiffTools.kt:138` — `runReadAction` call site on EDT
- `DiffTools.kt:228` — outer lambda inside the `addTools` coroutine
- The `actionPerformed` lambda is invoked directly by Swing on the EDT, with no `invokeLater` / `nonBlocking` wrapping.
Blocking reader (separate thread, `JobScheduler FJ pool 4/7`, RUNNABLE, holding a read permit via `tryRunReadAction`):
at java.base@25.0.2/java.util.regex.Pattern$Curly.match(...)
at java.base@25.0.2/java.util.regex.Pattern$Branch.match(...)
at java.base@25.0.2/java.util.regex.Pattern$Loop.match(...)
... ~270 frames of mutual regex recursion ...
at com.intellij.grazie.text.CheckerRunner.doRun(CheckerRunner.kt:105)
at com.intellij.grazie.text.CheckerRunner.run(CheckerRunner.kt:80)
at com.intellij.grazie.ide.inspection.grammar.GrazieInspection
$buildVisitor$1.visitElement(GrazieInspection.kt:86)
at com.intellij.psi.PsiElementVisitor.visitComment(PsiElementVisitor.java:35)
at com.intellij.psi.impl.source.tree.PsiCommentImpl.accept(PsiCommentImpl.java:27)
at com.intellij.codeInsight.daemon.impl.InspectionRunner ...
(standard inspection-runner harness above, running under tryRunReadAction)
The Grazie regex backtracking issue is itself worth reporting to JetBrains YouTrack (it's a sibling of `IJPL-158337`), but it is **not** what this issue is about — this issue is about the Claude Code plugin blocking the EDT, which makes the plugin fragile to any slow reader anywhere in the IDE.
Steps to Reproduce
- Open a Go project in GoLand on macOS with the Claude Code [Beta] plugin installed and the bundled Grazie plugin enabled (default).
- Ensure the Proofreading inspection is enabled (Settings → Editor → Inspections → Proofreading). This is the default.
- Edit a source file that contains a moderately long comment block.
- Use Claude Code in the integrated terminal to make an edit to that file via the
Edittool, which triggers the IDE diff viewer. - Click Accept in the diff viewer while Grazie's inspection pass is still running on the file (i.e., immediately when the diff appears, before the file has been fully analysed).
- Observe: GoLand freezes. Mouse and keyboard input are dropped. The "Not Responding" spinner appears. The freeze lasts until either the inspection finishes (can be many seconds to minutes for catastrophic backtracking) or the IDE is force-killed.
The freeze is intermittent because it requires the read lock to be held by a slow reader at the precise moment Accept is clicked.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.152
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
GoLand integrated terminal (zsh)
Additional Information
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗