Writing · Platform freezes
A non-cancellable read action in IntelliJ’s own test runner — and how it got fixed
A background task wrapped half its work in a cancellable read action and the other half in a blocking one, with no cancellation check in between. That is a textbook UI-freeze pattern, and it was sitting in IntelliJ’s own test runner.
What I was looking for
One of the plugins in my catalog, Background ReadAction Freeze Companion, is a static check for one specific mistake: a blocking read action on a background thread with no checkCanceled(). JetBrains’ own Platform Blog names that pattern as a recurring cause of UI freezes, because a pending write action — you typing in the editor — has to wait for the read action to finish. I ran the same check as a scan over IntelliJ Community’s own master.
What it found
SearchForTestsTask is a Task.Backgroundable. Its run() wrapped search() in a cancellable ReadAction.nonBlocking() — the right call — and then, a few lines later, called the sibling onFound() through a blocking ReadAction.run(), with no checkCanceled() anywhere in the class. So half the task could be cancelled and half could not.
I filed it as IDEA-393501 (originally IJPL-254654). JetBrains moved it into the IDEA project, under Java › Tests, and assigned it to a developer.
The part that made it harder than it looked
A blunt fix — swap the blocking call for a non-blocking read action — would have introduced a different bug. A non-blocking read action can be restarted, and onFound() writes files; on a restart it would write them twice. In a follow-up comment I added two points: that the default smart-mode branch was also blocking, through the deprecated DumbService.runReadActionInSmartMode() (which uses ReadAction.computeBlocking() internally), and that the real fix had to keep the write side safe.
How JetBrains fixed it
On October 28, 2026, the fix landed on master (commit 4a88fdfa): onFound() now runs in a non-blocking read action in smart mode, the loops that build the test lists call checkCanceled(), and the JUnit upload step moved to onSuccess(). The ticket is marked Fixed.
Why I write these up
This is the whole idea of Gap Hunter Labs in one ticket: a real bug, in code that many people depend on, found by a check I can point you to, reported with enough analysis to be actionable, and fixed by the people who own the code. The full set of platform tickets lives in Engineering & Evidence.