mastra-ai/mastra · error · HTTPException

Tool call id is required

Error message

Tool call id is required

What it means

The POST /agents/:agentId/approve-tool-call route requires a runId and toolCallId in the request body. The server validates them before resuming the agent run and throws HTTPException 400 when either is missing or empty. toolCallId identifies which pending tool invocation is being approved.

Source

Thrown at packages/server/src/server/handlers/agents.ts:2533

  responseSchema: toolCallResponseSchema,
  summary: 'Approve tool call',
  description: 'Approves a pending tool call and continues agent execution',
  tags: ['Agents', 'Tools'],
  requiresAuth: true,
  handler: async ({ mastra, agentId, abortSignal, requestContext, ...params }) => {
    try {
      const agent = await getAgentFromSystem({
        mastra,
        agentId,
        versionOptions: extractVersionOptions(requestContext),
      });

      if (!params.runId) {
        throw new HTTPException(400, { message: 'Run id is required' });
      }

      if (!params.toolCallId) {
        throw new HTTPException(400, { message: 'Tool call id is required' });
      }

      // UI Frameworks may send "client tools" in the body,
      // but it interferes with llm providers tool handling, so we remove them
      sanitizeBody(params, ['tools', 'actor']);

      await validateDurableToolCallAccess({
        mastra,
        agent,
        runId: params.runId,
        toolCallId: params.toolCallId,
        requestContext,
      });

      const streamResult = await agent.approveToolCall({
        ...params,
        requestContext,
        abortSignal,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Add toolCallId (the id of the suspended tool call, from the suspend chunk / suspended-runs response) to the request body
  2. Verify the body matches approveToolCallBodySchema — send JSON with at least { runId, toolCallId }
  3. Check the field is not an empty string; falsy values fail the check
  4. Update @mastra/client-js / playground UI to a version that sends toolCallId

Example fix

// before
await fetch(`/api/agents/${agentId}/approve-tool-call`, { method: 'POST', body: JSON.stringify({ runId }) });
// after
await fetch(`/api/agents/${agentId}/approve-tool-call`, { method: 'POST', body: JSON.stringify({ runId, toolCallId: pendingToolCall.id }) });
Defensive patterns

Strategy: validation

Validate before calling

if (!runId || !toolCallId) throw new Error('approve-tool-call requires runId and toolCallId');

Type guard

function hasToolCallInput(p: unknown): p is { runId: string; toolCallId: string } {
  return typeof p === 'object' && p !== null && typeof (p as any).runId === 'string' && (p as any).runId.length > 0 && typeof (p as any).toolCallId === 'string' && (p as any).toolCallId.length > 0;
}

Try / catch

try { await approveToolCall({ runId, toolCallId }); } catch (e) { if (e instanceof MastraClientError && e.status === 400) console.error('Missing runId/toolCallId:', e.message); else throw e; }

Prevention

When it happens

Trigger: POST to /agents/:agentId/approve-tool-call with a body omitting toolCallId (or sending it as empty string/undefined). The runId check on the same handler passes but toolCallId is absent.

Common situations: Hand-rolled curl/fetch calls against the approve endpoint; client SDK versions built before toolCallId was required; UI code forwarding only runId after a suspend event; copying a payload from a 'decline' flow that used different field names.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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