{"record":{"id":"e8a2e912e8d63897","repo":"mastra-ai/mastra","slug":"agent-send-tool-approval-no-active-thread-run","errorCode":"AGENT_SEND_TOOL_APPROVAL_NO_ACTIVE_THREAD_RUN","errorMessage":"Agent \"${this.name}\" sendToolApproval() could not find an active or suspended run for thread \"${threadId}\". The run may have already completed or been resumed.","messagePattern":"Agent \"(.+?)\" sendToolApproval\\(\\) could not find an active or suspended run for thread \"(.+?)\"\\. The run may have already completed or been resumed\\.","errorType":"error_code","errorClass":"MastraError","httpStatus":null,"severity":"error","filePath":"packages/core/src/agent/agent.ts","lineNumber":9483,"sourceCode":"          category: ErrorCategory.USER,\n          text:\n            `Agent \"${this.name}\" sendToolApproval() found ${matchingRuns.length} suspended runs for thread \"${threadId}\". ` +\n            `Pass a toolCallId to disambiguate, or resume a specific run with approveToolCall()/declineToolCall() and an explicit runId.`,\n          details: {\n            threadId,\n            resourceId,\n            agentName: this.name,\n            runIds: matchingRuns.map(run => run.runId).join(', '),\n          },\n        });\n      }\n\n      runId = matchingRuns[0]?.runId;\n      resolvedFromStorage = runId !== undefined;\n    }\n\n    if (!runId) {\n      throw new MastraError({\n        id: 'AGENT_SEND_TOOL_APPROVAL_NO_ACTIVE_THREAD_RUN',\n        domain: ErrorDomain.AGENT,\n        category: ErrorCategory.USER,\n        text:\n          `Agent \"${this.name}\" sendToolApproval() could not find an active or suspended run for thread \"${threadId}\". ` +\n          `The run may have already completed or been resumed.`,\n        details: {\n          threadId,\n          resourceId,\n          agentName: this.name,\n        },\n      });\n    }\n\n    const resumeOptions = deepMerge(\n      (streamOptions ?? {}) as Record<string, unknown>,\n      executionOptions as Record<string, unknown>,\n    ) as unknown as AgentExecutionOptions<OUTPUT>;","sourceCodeStart":9465,"sourceCodeEnd":9501,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/agent/agent.ts#L9465-L9501","documentation":"Agent.sendToolApproval() first tries to resolve a runId from options; if absent, it looks up an active or suspended run for the thread (resolvedFromStorage). When neither path yields a runId, there is nothing to approve against, so it throws. The run may have finished, been resumed, or never existed for that thread.","triggerScenarios":"Calling agent.sendToolApproval({ threadId }) with no runId/toolCallId when the thread has zero active or suspended runs — e.g. the run already completed, was already approved/resumed, or the threadId is wrong.","commonSituations":"Approving a tool call after the stream already finished; double-submitting an approval (first call resumed the run); approving on a thread belonging to another resource/agent; approving after a deployment restart where in-memory active runs were lost and storage holds no suspended run.","solutions":["Re-check the thread's state: confirm there is a pending tool approval before calling sendToolApproval.","If the run already completed, restart the conversation/turn instead of approving.","Make the approval flow idempotent: track which approvals were already sent and skip duplicates.","Verify threadId/resourceId correctness so the lookup targets the right thread."],"exampleFix":"// before\nawait agent.sendToolApproval({ threadId }); // assumes a pending run exists\n// after\nconst suspended = await agent.getSuspendedRuns?.({ threadId }); // or check storage\nif (suspended?.length) {\n  await agent.sendToolApproval({ threadId, toolCallId: suspended[0].toolCalls[0].toolCallId });\n}","handlingStrategy":"validation","validationCode":"const hasPending = await threadHasPendingToolApproval(threadId); // check suspended runs first\nif (!hasPending) return; // nothing to approve; skip silently","typeGuard":null,"tryCatchPattern":"try {\n  await agent.sendToolApproval({ threadId, toolCallId });\n} catch (e) {\n  if (String(e?.id) === 'AGENT_SEND_TOOL_APPROVAL_NO_ACTIVE_THREAD_RUN') {\n    // already completed/resumed: mark approval as stale and continue\n    markApprovalResolved(threadId, toolCallId);\n  } else throw e;\n}","preventionTips":["Check the thread has a pending suspended run before showing the approval UI.","Make approval submissions idempotent; disable the approve button after first submit.","Verify threadId/resourceId correctness for the environment (dev vs prod storage)."],"tags":["agent","tool-approval","no-active-run","state-not-found","user-input"],"backgroundTag":"suspended-run-not-found","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}