quarkusio/quarkus · error

A task is already pending

Error message

A task is already pending

What it means

evaluateInRenderThread() allows only one in-flight evaluation per RemoteThread. It throws IllegalStateException('A task is already pending') when a second evaluation is scheduled before the previous one's pendingTask is consumed by the render thread.

Source

Thrown at independent-projects/qute/debug/src/main/java/io/quarkus/qute/debug/agent/RemoteThread.java:367

     * compatibility with request-bound state.
     * </p>
     *
     * @param action the task to execute within the render thread context.
     *        It should return a {@link CompletableFuture} representing the computation result.
     * @return a {@link CompletableFuture} containing the result of the executed task
     * @throws IllegalStateException if the thread is not currently suspended or if another
     *         evaluation task is already pending
     */
    public CompletableFuture<Object> evaluateInRenderThread(Callable<CompletableFuture<Object>> action) {
        synchronized (lock) {
            // Ensure the render thread is suspended before executing any task
            if (this.state != DebuggerState.SUSPENDED) {
                throw new IllegalStateException("Thread not suspended");
            }

            // Prevent concurrent evaluation tasks from overlapping
            if (pendingTask != null) {
                throw new IllegalStateException("A task is already pending");
            }

            // Schedule the evaluation task to be executed by the render thread
            pendingTask = action;
            taskResult = null;

            // Wake up the suspended render thread so it can pick up and execute the task
            lock.notifyAll();

            // Wait for the render thread to process and complete the pending task
            boolean intr = false;
            try {
                while (pendingTask != null) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        intr = true;
                    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Serialize evaluations: wait for the previous evaluateInRenderThread() CompletableFuture to complete before submitting another.
  2. Catch IllegalStateException and retry after the pending task finishes.
  3. Queue evaluations per thread instead of issuing them concurrently.
  4. Investigate why the previous evaluation never completed (a blocking/long-running expression can wedge pendingTask).

Example fix

// before
thread.evaluateInRenderThread(a1);
thread.evaluateInRenderThread(a2); // throws
// after
thread.evaluateInRenderThread(a1).thenRun(() -> thread.evaluateInRenderThread(a2));
Defensive patterns

Strategy: validation

Validate before calling

synchronized (thread) {
    if (pendingEvaluationInFlight) throw new IllegalStateException("serialize evaluations");
    pendingEvaluationInFlight = true;
}
thread.evaluateInRenderThread(action).whenComplete((r, e) -> pendingEvaluationInFlight = false);

Try / catch

try {
    return thread.evaluateInRenderThread(action).get();
} catch (IllegalStateException e) {
    if ("A task is already pending".equals(e.getMessage())) {
        // queue or retry after the current evaluation completes
    }
    throw e;
}

Prevention

When it happens

Trigger: Issuing two overlapping evaluate requests on the same suspended render thread — e.g. a DAP client sending multiple evaluateRequests (watch expressions, hover, repl) before the first completes.

Common situations: IDE watch panels firing several evaluations at once on breakpoint hit; batch variable evaluation scripts; a slow evaluation (blocking expression) causing the UI to re-issue requests.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/cb7842157f9d8ea5. Report an issue: GitHub.