alibaba/arthas · error · McpError

-32603

-32603

Error message

Task did not complete within timeout

What it means

Thrown by DefaultTaskManager when a task does not reach a terminal state before the configured timeout elapses. It builds a McpError with ErrorCodes.INTERNAL_ERROR (-32603) and the task ID as data. It can surface in two places: when waiting on a terminal task that times out, and in the exceptionally handler when a TimeoutException is caught and rethrown as a RuntimeException wrapping the McpError.

Source

Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/task/DefaultTaskManager.java:464

                                .message("Task did not complete within timeout")
                                .data("Task ID: " + taskId)
                                .build());
                        return failed;
                    }
                    McpSchema.Task terminalTask = updates.get(updates.size() - 1);
                    if (!terminalTask.isTerminal()) {
                        CompletableFuture<McpSchema.Result> failed = new CompletableFuture<>();
                        failed.completeExceptionally(McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
                                .message("Task did not complete within timeout")
                                .data("Task ID: " + taskId)
                                .build());
                        return failed;
                    }
                    return fetchTaskResult(taskId, sessionId);
                })
                .exceptionally(ex -> {
                    if (ex instanceof TimeoutException || ex.getCause() instanceof TimeoutException) {
                        throw new RuntimeException(
                                McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
                                        .message("Task did not complete within timeout")
                                        .data("Task ID: " + taskId)
                                        .build());
                    }
                    throw new RuntimeException(ex);
                });
    }

    /**
     * Processes all queued side-channel messages for an INPUT_REQUIRED task, then waits for terminal state.
     */
    private CompletableFuture<McpSchema.Result> processQueuedMessagesAndWaitForTerminal(
            TaskManagerHost.TaskHandlerContext ctx, String taskId, String sessionId) {
        logger.debug("processQueuedMessagesAndWaitForTerminal: Starting side-channel processing for task {}", taskId);

        return processAllQueuedMessages(ctx, taskId)
                .thenCompose(v -> {

View on GitHub (pinned to 21cf2e9ba5)

Solutions

  1. Increase the effective wait/timeout budget so it exceeds the task's realistic worst-case duration.
  2. Ensure the task handler always reaches a terminal state (completed/failed/cancelled) even on error paths.
  3. Make the handler cancel-aware so cancellation/interrupts propagate to the underlying work.
  4. Add task-level heartbeats or progress updates and surface partial results instead of blocking indefinitely.

Example fix

// before
return taskTool.createTaskHandler().createTask(args, extra); // may hang

// after
Future<McpSchema.Result> f = taskTool.createTaskHandler().createTask(args, extra);
try {
    return f.get(extendedTimeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException te) {
    taskStore.requestCancellation(taskId, sessionId);
    throw te;
}
Defensive patterns

Strategy: retry

Validate before calling

// Size the wait budget to the task's worst case before invoking
long budget = Math.max(configuredTimeout, expectedMaxMillis * 2);
// then poll/wait with that budget

Try / catch

try {
    future.get(budget, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
    Throwable c = e.getCause();
    if (c instanceof McpError me && me.getMessage().contains("timeout")) {
        // retry once with a larger budget, or request cancellation
        taskStore.requestCancellation(taskId, sessionId);
    }
    throw e;
} catch (TimeoutException te) {
    taskStore.requestCancellation(taskId, sessionId);
    throw te;
}

Prevention

When it happens

Trigger: A long-running task-aware tool whose handler never completes within the wait/poll budget; the task's createTaskHandler blocks indefinitely; the task message queue stalls so terminal state is never reached; the polling window is shorter than the real work duration.

Common situations: External dependency (DB, network, subprocess) hanging; handler forgot to call a completion method; tight timeouts configured for heavy work; lost wakeups in the message queue.

Understand the failure class

Related errors


AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14). Data as JSON: /api/errors/32f9aa40db727992. Report an issue: GitHub.