mastra-ai/mastra · error · MastraError

AGENT_SEND_TOOL_APPROVAL_AMBIGUOUS_SUSPENDED_RUNS

AGENT_SEND_TOOL_APPROVAL_AMBIGUOUS_SUSPENDED_RUNS

Error message

Agent "${this.name}" sendToolApproval() found ${matchingRuns.length} suspended runs for thread "${threadId}". Pass a toolCallId to disambiguate, or resume a specific run with approveToolCall()/declineToolCall() and an explicit runId.

What it means

Agent.sendToolApproval() collects all suspended runs for the thread, optionally filtered by toolCallId. If more than one run still matches, the target of the approval is ambiguous, so the library refuses to guess and throws. The error explicitly tells you how to disambiguate: pass a toolCallId, or use approveToolCall()/declineToolCall() with an explicit runId.

Source

Thrown at packages/core/src/agent/agent.ts:9462

      // suspended-run discovery so approvals stay durable.
      let suspendedRuns: AgentRun[] = [];
      try {
        ({ runs: suspendedRuns } = await this.listSuspendedRuns({ threadId, resourceId }));
      } catch (error) {
        // Only swallow the expected no-storage case — storage outages and
        // store-driver errors must surface instead of masquerading as
        // "no suspended run exists".
        if (!(error instanceof MastraError) || error.id !== 'AGENT_LIST_SUSPENDED_RUNS_NO_STORAGE') {
          throw error;
        }
      }

      const matchingRuns = options.toolCallId
        ? suspendedRuns.filter(run => run.toolCalls.some(toolCall => toolCall.toolCallId === options.toolCallId))
        : suspendedRuns;

      if (matchingRuns.length > 1) {
        throw new MastraError({
          id: 'AGENT_SEND_TOOL_APPROVAL_AMBIGUOUS_SUSPENDED_RUNS',
          domain: ErrorDomain.AGENT,
          category: ErrorCategory.USER,
          text:
            `Agent "${this.name}" sendToolApproval() found ${matchingRuns.length} suspended runs for thread "${threadId}". ` +
            `Pass a toolCallId to disambiguate, or resume a specific run with approveToolCall()/declineToolCall() and an explicit runId.`,
          details: {
            threadId,
            resourceId,
            agentName: this.name,
            runIds: matchingRuns.map(run => run.runId).join(', '),
          },
        });
      }

      runId = matchingRuns[0]?.runId;
      resolvedFromStorage = runId !== undefined;
    }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Pass the toolCallId of the specific tool call you are approving in options.toolCallId.
  2. Switch to agent.approveToolCall() or agent.declineToolCall() with an explicit runId to target one run.
  3. List the suspended runs for the thread and confirm which run/tool call you intend, then retry with the explicit identifiers.

Example fix

// before
await agent.sendToolApproval({ threadId });
// after
await agent.sendToolApproval({ threadId, toolCallId: 'call_abc123' });
// or
await agent.approveToolCall({ threadId, runId: 'run_456', toolCallId: 'call_abc123' });
Defensive patterns

Strategy: validation

Validate before calling

const suspended = await getSuspendedRunsForThread(threadId); // your storage lookup
if (suspended.length > 1 && !options.toolCallId) {
  throw new Error('Multiple suspended runs; supply toolCallId or explicit runId.');
}

Try / catch

try {
  await agent.sendToolApproval({ threadId, toolCallId });
} catch (e) {
  if (String(e?.id) === 'AGENT_SEND_TOOL_APPROVAL_AMBIGUOUS_SUSPENDED_RUNS') {
    // surface a picker to the user listing the suspended runs/tool calls
    showDisambiguationPrompt(e);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling agent.sendToolApproval({ threadId }) without toolCallId while the thread has multiple suspended runs; or passing a toolCallId that matches tool calls in more than one suspended run (duplicate toolCallIds across runs).

Common situations: Threads with several concurrently suspended agent runs (parallel tool calls or multiple resumed streams); callers storing only threadId and omitting toolCallId; tool call IDs reused after retries; bulk approval UI iterating a thread with several pending runs.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/5b11ad84a68a3776. Report an issue: GitHub.