mastra-ai/mastra · error · MastraError
AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED
AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED
Error message
Agent "${this.name}" ${method}() cannot resume tool call "${toolCallId}" because it is not suspended. What it means
A MastraError (id AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED) thrown when resuming an agentic loop run with a `toolCallId` that is not currently in a suspended state in the run's snapshot. Resume targets specific suspended tool calls; the library verified the snapshot's suspended tools and did not find this toolCallId among them.
Source
Thrown at packages/core/src/agent/agent.ts:6912
if (!isSuspended) {
// A resume stream can expose the next suspension just before its snapshot is
// persisted. Briefly poll after authorization so an immediate response to
// that newly surfaced tool call is not rejected based on the prior snapshot.
const effectiveMastra = this.#mastra ?? (await this.#getOrCreateEphemeralMastra());
const workflowsStore = await effectiveMastra?.getStorage()?.getStore('workflows');
const deadline = Date.now() + 2000;
while (!isSuspended && workflowsStore && Date.now() < deadline) {
await new Promise(resolve => setTimeout(resolve, 25));
const latestSnapshot = await workflowsStore.loadWorkflowSnapshot({ workflowName: 'agentic-loop', runId });
if (latestSnapshot && isTargetSuspended(latestSnapshot)) {
resumeSnapshot = latestSnapshot;
isSuspended = true;
}
}
}
if (!isSuspended) {
throw new MastraError({
id: 'AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED',
domain: ErrorDomain.AGENT,
category: ErrorCategory.USER,
text: `Agent "${this.name}" ${method}() cannot resume tool call "${toolCallId}" because it is not suspended.`,
details: {
agentName: this.name,
method,
runId,
toolCallId,
},
});
}
return resumeSnapshot;
}
/**
* Suspend payloads persisted before they carried `toolCallId` only hold theView on GitHub (pinned to 75dd419e61)
Solutions
- Use the exact toolCallId from the suspend event payload of the currently suspended run
- Check the run snapshot/status to list which tool calls are still suspended before resuming
- Guard against double-resume (disable the resume action after the first successful call)
- Ensure the id is the tool call id (not tool name or tool execution id)
- If the run already completed, start a new run instead of resuming
Example fix
// before
await agent.resume({ runId, toolCallId: 'humanApprovalTool' }); // tool name, not call id
// after
await agent.resume({ runId, toolCallId: suspendEvent.payload.toolCallId }); Defensive patterns
Strategy: validation
Validate before calling
function validateResumePayload(suspendEvent, runId, toolCallId) {
if (suspendEvent.runId !== runId) throw new Error('runId mismatch');
const suspended = suspendEvent.payload?.suspendedToolCallIds ?? [suspendEvent.payload?.toolCallId];
if (!suspended.includes(toolCallId)) {
throw new Error(`toolCallId ${toolCallId} is not suspended; suspended: ${suspended.join(', ')}`);
}
} Type guard
function isSuspendedToolCall(toolCallId, suspendedIds) {
return typeof toolCallId === 'string' && suspendedIds.includes(toolCallId);
} Try / catch
try {
return await agent.resume({ runId, toolCallId, resumeData });
} catch (e) {
if (e?.id === 'AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED') {
logger.warn(`toolCallId ${toolCallId} already resumed or invalid; refresh run state`);
return refreshRunState(runId);
}
throw e;
} Prevention
- Take toolCallId only from the current suspend event payload
- Disable resume UI actions once a tool call has been resumed (idempotency guard)
- Distinguish tool names from tool call ids in client state
- Re-fetch run/snapshot state before showing resume options to users
When it happens
Trigger: Calling `agent.resume({ runId, toolCallId, ... })` where the toolCallId belongs to a tool that already resumed/completed, a tool that never suspends, or a fabricated/mismatched id; also when the suspend point is in a different tool than the one addressed.
Common situations: UIs resuming with stale toolCallIds after a prior resume already succeeded; resuming multiple tool calls where one was already handled; persisting toolCallIds client-side and reusing them across retries; passing a tool *name* instead of the tool *call* id.
Related errors
- AGENT_RESUME_NO_SNAPSHOT_FOUND
- tool_result must be preceded by a tool_call
- AGENT_SEND_STREAM_RESUME_NO_SUSPENDED_THREAD_RUN
- No active suspension to resume
- Cannot resume a suspended tool without a current thread
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/a41ef68611c22753.
Report an issue: GitHub.