claude-code icon indicating copy to clipboard operation
claude-code copied to clipboard

[BUG] [platform::intellij] EDT Slow Operation Error in DiagnosticTools causing IDE freezes

Open coleleavitt opened this issue 1 month ago • 4 comments

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 plugin triggers a "Slow operations are prohibited on EDT" error when collecting diagnostics. This causes IDE performance issues and stack trace errors in the logs. The error occurs in DiagnosticTools.java when the daemon code analyzer finishes and attempts to check if error analysis is complete on the Event Dispatch Thread (EDT).

What Should Happen?

The diagnostic collection should happen on a background thread to avoid blocking the UI thread. The plugin should use IntelliJ's ReadAction.nonBlocking() or similar background execution patterns to perform file system and indexing operations without freezing the IDE.

Error Messages/Logs

java.lang.Throwable: Slow operations are prohibited on EDT. See SlowOperations.assertSlowOperationsAreAllowed javadoc.
    at com.intellij.openapi.diagnostic.Logger.error(Logger.java:375)
    at com.intellij.util.SlowOperations.logError(SlowOperations.java:180)
    at com.intellij.util.SlowOperations.assertSlowOperationsAreAllowed(SlowOperations.java:128)
    at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.ensureIsUpToDate(WorkspaceFileIndexDataImpl.kt:232)
    at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexDataImpl.getFileInfo(WorkspaceFileIndexDataImpl.kt:176)
    at com.intellij.workspaceModel.core.fileIndex.impl.WorkspaceFileIndexImpl.getFileInfo(WorkspaceFileIndexImpl.kt:357)
    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)

Steps to Reproduce

  1. Install Claude Code plugin version 0.1.12-beta
  2. Open any Java/Kotlin project in IntelliJ IDEA
  3. Edit a file to trigger code analysis
  4. Wait for daemon analysis to complete
  5. Check IDE logs - the EDT slow operation error appears
  6. IDE may feel sluggish during diagnostic collection

Claude Model

None

Is this a regression?

I don't know

Last Working Version

0.1.12-beta

Claude Code Version

n/a

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

Other

Additional Information

Suspected cause: The plugin calls java daemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile) on EDT in the daemonFinished() callback, which triggers workspace file index updates (slow I/O operations)

Location: DiagnosticTools.kt line 106 (decompiled to DiagnosticTools.java line 204) Impact: UI freezes, performance degradation during code analysis

Proposed Solution: Use ReadAction.nonBlocking() or ApplicationManager.getApplication().executeOnPooledThread() to move slow operations off EDT

// Instead of calling directly on EDT:
if (daemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)) {
    // ... diagnostic collection
}

// Use background execution:
ReadAction.nonBlocking {
    if (daemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)) {
        computeDiagnostics()
    } else emptyList()
}.withDocumentsCommitted(project)
.finishOnUiThread(ModalityState.defaultModalityState()) { diagnostics ->
    // Process results on EDT
    processDiagnostics(diagnostics)
}.submit(AppExecutorUtil.getAppExecutorService())

DiagnosticTools.java

coleleavitt avatar Nov 01 '25 18:11 coleleavitt

@hackyon-anthropic

coleleavitt avatar Nov 01 '25 18:11 coleleavitt

9a10
> import com.intellij.concurrency.AppExecutorUtil;
11a13,14
> import com.intellij.openapi.application.ModalityState;
> import com.intellij.openapi.application.ReadAction;
28a32
> import java.util.Collections;
75a80
>       
90a96
>                   
92c98
<                      ApplicationManager.getApplication().invokeAndWait(<undefinedtype>::invokeSuspend$lambda$1);
---
>                      ApplicationManager.getApplication().invokeAndWait(() -> invokeSuspend$lambda$1(callToolRequest, project, projectDir, result, mcpService));
128c134,138
<                         return (Continuation)(new <anonymous constructor>($completion));
---
>                         return (Continuation)(new Function2(value, $completion) {
>                            public final Object invokeSuspend(Object $result) {
>                               return this.invokeSuspend($result);
>                            }
>                         });
132c142
<                         return ((<undefinedtype>)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);
---
>                         return ((Function2)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);
159c169,173
<             Function2 var3 = new <anonymous constructor>($completion);
---
>             Function2 var3 = new Function2(value, $completion) {
>                public final Object invokeSuspend(Object $result) {
>                   return this.invokeSuspend($result);
>                }
>             };
165c179
<             return ((<undefinedtype>)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);
---
>             return ((Function2)this.create(p1, p2)).invokeSuspend(Unit.INSTANCE);
174a189
>                
187d201
<                   int var10 = 0;
201a216
>                   
204,217c219,231
<                         if (daemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)) {
<                            connection.disconnect();
<                            Document var10000 = psiFile.getFileDocument();
<                            Intrinsics.checkNotNullExpressionValue(var10000, "getFileDocument(...)");
<                            Document document = var10000;
<                            List diagnostics = (List)(new ArrayList());
<                            DaemonCodeAnalyzerEx.processHighlights(document, $project, HighlightSeverity.WEAK_WARNING, 0, document.getTextLength(), <undefinedtype>::daemonFinished$lambda$1);
<                            Json this_$iv = (Json)Json.Default;
<                            FileDiagnostic var7 = new FileDiagnostic;
<                            String var10002 = args.getUri();
<                            if (var10002 == null) {
<                               var10002 = file.getPath();
<                               Intrinsics.checkNotNullExpressionValue(var10002, "getPath(...)");
<                               var10002 = UtilsKt.pathToFileUri(var10002);
---
>                         // FIX: Move slow operation to background thread using ReadAction.nonBlocking
>                         ReadAction.nonBlocking(() -> {
>                            // This runs on background thread - safe for slow operations
>                            if (daemonCodeAnalyzer.isErrorAnalyzingFinished(psiFile)) {
>                               Document var10000 = psiFile.getFileDocument();
>                               Intrinsics.checkNotNullExpressionValue(var10000, "getFileDocument(...)");
>                               Document document = var10000;
>                               
>                               List<Diagnostic> diagnostics = new ArrayList<>();
>                               DaemonCodeAnalyzerEx.processHighlights(document, $project, HighlightSeverity.WEAK_WARNING, 0, document.getTextLength(), 
>                                  (info) -> daemonFinished$lambda$0(document, diagnostics, info));
>                               
>                               return diagnostics;
218a233,248
>                            return Collections.<Diagnostic>emptyList();
>                         })
>                         .withDocumentsCommitted($project)
>                         .finishOnUiThread(ModalityState.defaultModalityState(), (diagnostics) -> {
>                            // This runs on EDT - safe for UI operations
>                            try {
>                               connection.disconnect();
>                               
>                               Json this_$iv = (Json)Json.Default;
>                               FileDiagnostic var7 = new FileDiagnostic();
>                               String var10002 = args.getUri();
>                               if (var10002 == null) {
>                                  var10002 = file.getPath();
>                                  Intrinsics.checkNotNullExpressionValue(var10002, "getPath(...)");
>                                  var10002 = UtilsKt.pathToFileUri(var10002);
>                               }
220,227c250,261
<                            var7.<init>(var10002, diagnostics);
<                            List value$iv = CollectionsKt.listOf(var7);
<                            int $i$f$encodeToString = 0;
<                            this_$iv.getSerializersModule();
<                            String results = this_$iv.encodeToString((SerializationStrategy)(new ArrayListSerializer(FileDiagnostic.Companion.serializer())), value$iv);
<                            $result.complete(new CallToolResult(CollectionsKt.listOf(new TextContent(results)), (Boolean)null, (JsonObject)null, 6, (DefaultConstructorMarker)null));
<                         }
< 
---
>                               var7.setUri(var10002);
>                               var7.setDiagnostics(diagnostics);
>                               List value$iv = CollectionsKt.listOf(var7);
>                               int $i$f$encodeToString = 0;
>                               this_$iv.getSerializersModule();
>                               String results = this_$iv.encodeToString((SerializationStrategy)(new ArrayListSerializer(FileDiagnostic.Companion.serializer())), value$iv);
>                               $result.complete(new CallToolResult(CollectionsKt.listOf(new TextContent(results)), (Boolean)null, (JsonObject)null, 6, (DefaultConstructorMarker)null));
>                            } catch (Exception e) {
>                               $result.complete(new CallToolResult(CollectionsKt.listOf(new TextContent("Error processing diagnostics: " + e.getMessage())), (Boolean)null, (JsonObject)null, 6, (DefaultConstructorMarker)null));
>                            }
>                         })
>                         .submit(AppExecutorUtil.getAppExecutorService());
230c264
<                      private static final boolean daemonFinished$lambda$0(Document $document, List $diagnostics, HighlightInfo info) {
---
>                      private static final boolean daemonFinished$lambda$0(Document $document, List<Diagnostic> $diagnostics, HighlightInfo info) {
243d276
< 
246,249d278
< 
<                      private static final boolean daemonFinished$lambda$1(Function1 $tmp0, Object p0) {
<                         return (Boolean)$tmp0.invoke(p0);
<                      }
250a280
>                   
254c284
<                $result.complete(new CallToolResult(CollectionsKt.listOf(new TextContent("Error getting diagnostics")), (Boolean)null, (JsonObject)null, 6, (DefaultConstructorMarker)null));
---
>                $result.complete(new CallToolResult(CollectionsKt.listOf(new TextContent("Error getting diagnostics: " + var11.getMessage())), (Boolean)null, (JsonObject)null, 6, (DefaultConstructorMarker)null));
256d285
< 
260a290
> 

coleleavitt avatar Nov 01 '25 18:11 coleleavitt

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

github-actions[bot] avatar Dec 09 '25 10:12 github-actions[bot]

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

active

coleleavitt avatar Dec 09 '25 16:12 coleleavitt