{"record":{"id":"a41ef68611c22753","repo":"mastra-ai/mastra","slug":"agent-resume-tool-call-not-suspended","errorCode":"AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED","errorMessage":"Agent \"${this.name}\" ${method}() cannot resume tool call \"${toolCallId}\" because it is not suspended.","messagePattern":"Agent \"(.+?)\" (.+?)\\(\\) cannot resume tool call \"(.+?)\" because it is not suspended\\.","errorType":"error_code","errorClass":"MastraError","httpStatus":null,"severity":"error","filePath":"packages/core/src/agent/agent.ts","lineNumber":6912,"sourceCode":"    if (!isSuspended) {\n      // A resume stream can expose the next suspension just before its snapshot is\n      // persisted. Briefly poll after authorization so an immediate response to\n      // that newly surfaced tool call is not rejected based on the prior snapshot.\n      const effectiveMastra = this.#mastra ?? (await this.#getOrCreateEphemeralMastra());\n      const workflowsStore = await effectiveMastra?.getStorage()?.getStore('workflows');\n      const deadline = Date.now() + 2000;\n      while (!isSuspended && workflowsStore && Date.now() < deadline) {\n        await new Promise(resolve => setTimeout(resolve, 25));\n        const latestSnapshot = await workflowsStore.loadWorkflowSnapshot({ workflowName: 'agentic-loop', runId });\n        if (latestSnapshot && isTargetSuspended(latestSnapshot)) {\n          resumeSnapshot = latestSnapshot;\n          isSuspended = true;\n        }\n      }\n    }\n\n    if (!isSuspended) {\n      throw new MastraError({\n        id: 'AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED',\n        domain: ErrorDomain.AGENT,\n        category: ErrorCategory.USER,\n        text: `Agent \"${this.name}\" ${method}() cannot resume tool call \"${toolCallId}\" because it is not suspended.`,\n        details: {\n          agentName: this.name,\n          method,\n          runId,\n          toolCallId,\n        },\n      });\n    }\n\n    return resumeSnapshot;\n  }\n\n  /**\n   * Suspend payloads persisted before they carried `toolCallId` only hold the","sourceCodeStart":6894,"sourceCodeEnd":6930,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/agent/agent.ts#L6894-L6930","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait agent.resume({ runId, toolCallId: 'humanApprovalTool' }); // tool name, not call id\n// after\nawait agent.resume({ runId, toolCallId: suspendEvent.payload.toolCallId });","handlingStrategy":"validation","validationCode":"function validateResumePayload(suspendEvent, runId, toolCallId) {\n  if (suspendEvent.runId !== runId) throw new Error('runId mismatch');\n  const suspended = suspendEvent.payload?.suspendedToolCallIds ?? [suspendEvent.payload?.toolCallId];\n  if (!suspended.includes(toolCallId)) {\n    throw new Error(`toolCallId ${toolCallId} is not suspended; suspended: ${suspended.join(', ')}`);\n  }\n}","typeGuard":"function isSuspendedToolCall(toolCallId, suspendedIds) {\n  return typeof toolCallId === 'string' && suspendedIds.includes(toolCallId);\n}","tryCatchPattern":"try {\n  return await agent.resume({ runId, toolCallId, resumeData });\n} catch (e) {\n  if (e?.id === 'AGENT_RESUME_TOOL_CALL_NOT_SUSPENDED') {\n    logger.warn(`toolCallId ${toolCallId} already resumed or invalid; refresh run state`);\n    return refreshRunState(runId);\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["agent","resume","tool-call","suspend","state","runid"],"backgroundTag":"suspend-resume-snapshot-missing","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}