JetBrains/intellij-community · error · EvaluateException

Thread has been collected

Error message

Thread has been collected

What it means

Reported by StackFrameProxyImpl when asynchronously fetching a stack frame by index fails because the thread object was garbage-collected in the debuggee VM: JDI raises ObjectCollectedException, which is unwrapped from the future and translated into the localized evaluation.error.thread.collected message ('Thread has been collected'). The frame data is no longer reachable, so any inspection of it fails.

Source

Thrown at java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java:193

      return getFrameIndexAsync().thenCompose(index -> {
          // batch get frames from 1 to FRAMES_BATCH_MAX
          // making this number very high does not help much because renderers invocation usually flush all caches
          if (index > 0 && index < FRAMES_BATCH_MAX) {
            try {
              return DebuggerUtilsAsync.frames(threadRef, 0, Math.min(myThreadProxy.frameCount(), FRAMES_BATCH_MAX))
                .thenApply(frames -> myStackFrame = frames.get(index));
            }
            catch (EvaluateException e) {
              return CompletableFuture.failedFuture(e);
            }
          }
          else {
            return DebuggerUtilsAsync.frame(threadRef, index).thenApply(f -> myStackFrame = f);
          }
        })
        .exceptionally(throwable -> {
          if (DebuggerUtilsAsync.unwrap(throwable) instanceof ObjectCollectedException) {
            throw new CompletionException(EvaluateExceptionUtil.createEvaluateException(JavaDebuggerBundle.message("evaluation.error.thread.collected")));
          }
          throw (RuntimeException)throwable;
        });
    }

    return CompletableFuture.completedFuture(myStackFrame);
  }

  @Override
  public int getFrameIndex() throws EvaluateException {
    DebuggerManagerThreadImpl.assertIsManagerThread();
    checkValid();
    if (myFrameIndex == -1) {
      int count = myThreadProxy.frameCount();

      if (myFrameFromBottomIndex > count) {
        throw EvaluateExceptionUtil.createEvaluateException(new IncompatibleThreadStateException());
      }

View on GitHub (pinned to be881553f2)

Solutions

  1. Refresh the debugger session (resume and re-suspend) to obtain fresh thread/frame mirrors
  2. Restart the debug session if the thread has terminated — its frames cannot be recovered
  3. Keep the debuggee under less GC pressure / avoid full GC while paused (increase heap)
  4. Update the IDE: reference-retention of thread mirrors has seen fixes
Defensive patterns

Strategy: retry

Validate before calling

// before using a thread mirror across suspensions
if (!threadRef.isCollected()) {
  frames = DebuggerUtilsAsync.frame(threadRef, index).join();
} else {
  // drop the cached proxy; refresh thread list
}

Try / catch

frameFuture.exceptionally(t -> {
  if (DebuggerUtilsAsync.unwrap(t) instanceof ObjectCollectedException) {
    // re-fetch thread reference from vm.allThreads() and retry once
  }
  throw (RuntimeException) t;
});

Prevention

When it happens

Trigger: DebuggerUtilsAsync.frame(threadRef, index) (or a cached frames refresh) completes exceptionally; DebuggerUtilsAsync.unwrap(throwable) is an ObjectCollectedException — the ThreadReference was collected by the debuggee GC (references not kept / collection enabled on the mirror), and the exceptionally block rethrows EvaluateExceptionUtil.createEvaluateException(evaluation.error.thread.collected).

Common situations: Stepping or evaluating on a thread that finished and became garbage before the request was processed; debugger holding weak references after the thread terminated; heavy GC in the debuggee while the UI requests frames; long-paused session where mirrors were collected.

Related errors


AI-assisted analysis of JetBrains/intellij-community@be881553f2 (2026-08-14). Data as JSON: /api/errors/79e574fbf4fb93ff. Report an issue: GitHub.