alibaba/arthas · error · McpError
-32603
-32603
Error message
Maximum task limit reached ({}) What it means
Thrown by InMemoryTaskStore.createTask() when the number of stored tasks has reached maxTasks (default from the Builder). Creation is guarded by a synchronized createTaskLock and checks tasks.size() >= maxTasks; on overflow it throws a CompletionException wrapping a McpError with ErrorCodes.INTERNAL_ERROR (-32603). The limit caps memory use of the in-memory store.
Source
Thrown at arthas-mcp-server/src/main/java/com/taobao/arthas/mcp/server/task/InMemoryTaskStore.java:171
return new InMemoryTaskStore<>(defaultTtl, defaultPollInterval, messageQueue, maxTasks);
}
}
private final Object createTaskLock = new Object();
private boolean isSessionValid(TaskEntry entry, String requestSessionId) {
if (requestSessionId == null) return true;
String taskSessionId = entry.sessionId();
if (taskSessionId == null || taskSessionId.isEmpty()) return true;
return requestSessionId.equals(taskSessionId);
}
@Override
public CompletableFuture<McpSchema.Task> createTask(CreateTaskOptions options) {
return CompletableFuture.supplyAsync(() -> {
synchronized (createTaskLock) {
if (tasks.size() >= maxTasks) {
throw new CompletionException(
McpError.builder(McpSchema.ErrorCodes.INTERNAL_ERROR)
.message("Maximum task limit reached (" + maxTasks + ")")
.build()
);
}
String taskId = options.taskId() != null ? options.taskId() : UUID.randomUUID().toString();
String now = Instant.now().toString();
Long ttl = options.requestedTtl() != null ? options.requestedTtl() : defaultTtl;
Long pollInterval = options.pollInterval() != null ? options.pollInterval() : defaultPollInterval;
String sessionId = options.sessionId();
McpSchema.Task task = McpSchema.Task.builder()
.taskId(taskId)
.status(McpSchema.TaskStatus.WORKING)
.createdAt(now)
.lastUpdatedAt(now)
.ttl(ttl)View on GitHub (pinned to 21cf2e9ba5)
Solutions
- Raise maxTasks via InMemoryTaskStore.Builder().maxTasks(N) to match expected concurrency.
- Lower the task TTL so completed/expired tasks free slots sooner.
- Back-pressure the client: only create new tasks after prior ones finish or expire.
- Ensure tasks actually reach terminal state so they can be evicted; switch to a persistent TaskStore for large scale.
Example fix
// before
InMemoryTaskStore.Builder<McpSchema.ServerTaskPayloadResult> b =
new InMemoryTaskStore.Builder<>();
store = b.build(); // default maxTasks too low
// after
store = new InMemoryTaskStore.Builder<McpSchema.ServerTaskPayloadResult>()
.maxTasks(10_000)
.defaultTtl(Duration.ofMinutes(5).toMillis())
.build(); Defensive patterns
Strategy: retry
Validate before calling
// Check remaining capacity before creating
int remaining = maxTasks - store.size(); // if a size accessor exists
if (remaining <= 0) {
// back-pressure: wait for tasks to expire or finish
} Try / catch
// Retry creation after a short backoff once expired tasks free slots
try {
return store.createTask(options).join();
} catch (CompletionException e) {
Throwable c = e.getCause();
if (c instanceof McpError me && me.getMessage().contains("Maximum task limit")) {
Thread.sleep(pollInterval);
return store.createTask(options).join(); // retry once
}
throw e;
} Prevention
- Set maxTasks to match expected peak concurrency.
- Keep TTL short enough that tasks are evicted promptly.
- Back-pressure clients so they create tasks at a sustainable rate.
- Ensure tasks reach terminal state so slots free up.
When it happens
Trigger: Creating tasks faster than they expire (TTL eviction) or reach terminal state; long TTLs accumulating many tasks; a client loop spawning tasks without waiting for completion; maxTasks configured too low for the workload.
Common situations: Load test or buggy client flooding task creation; low maxTasks default in a multi-tenant server; TTL set so high that evictions never free slots; tasks stuck in WORKING forever consuming slots.
Related errors
AI-assisted analysis of alibaba/arthas@21cf2e9ba5 (2026-08-14).
Data as JSON: /api/errors/2bfaae0dd52558fd.
Report an issue: GitHub.