{"record":{"id":"44fa5653e799a167","repo":"mastra-ai/mastra","slug":"mastra-livekit-the-agent-requested-tool-approval","errorCode":null,"errorMessage":"@mastra/livekit: the agent requested tool approval or suspended a tool call; human-in-the-loop flows (approve-tool-call / resume-stream) are not supported on the voice path. Remove requireApproval or suspend from the tools this agent uses on voice calls.","messagePattern":"@mastra/livekit: the agent requested tool approval or suspended a tool call; human-in-the-loop flows \\(approve-tool-call / resume-stream\\) are not supported on the voice path\\. Remove requireApproval or suspend from the tools this agent uses on voice calls\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integrations/livekit/src/remote.ts","lineNumber":339,"sourceCode":"                    break;\n                  }\n                  case 'finish': {\n                    clearWatchdog();\n                    const output = payload.output as { usage?: unknown } | undefined;\n                    const turnUsage = mapTurnUsage(output?.usage);\n                    if (turnUsage) {\n                      usage = turnUsage;\n                      try {\n                        ctx.onUsage?.(turnUsage);\n                      } catch (error) {\n                        console.warn('@mastra/livekit: onUsage hook threw', error);\n                      }\n                    }\n                    break;\n                  }\n                  case 'tool-call-approval':\n                  case 'tool-call-suspended':\n                    throw new Error(HITL_UNSUPPORTED_MESSAGE);\n                  case 'error': {\n                    const error = payload.error;\n                    throw error instanceof Error ? error : new Error(String(error));\n                  }\n                  default:\n                    break; // ignore everything else (text-start, step-start, tool-result, ...)\n                }\n              }\n              clearWatchdog();\n              break; // success, or a clean barge-in break out of the SSE loop\n            } catch (error) {\n              clearWatchdog();\n              if (cancelled) break; // barge-in: not a failure, emit interrupted below\n\n              // Classify into the LiveKit error vocabulary. Application errors thrown after streaming\n              // began (an `error` chunk, a HITL chunk) are not connection failures — propagate as-is.\n              let typed: APIError;\n              if (timedOut) {","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/integrations/livekit/src/remote.ts#L321-L357","documentation":"While consuming the Mastra agent's stream, the generator received a 'tool-call-approval' or 'tool-call-suspended' event, meaning the agent uses a tool with requireApproval or a suspend-based human-in-the-loop flow. Voice paths have no UI to approve/resume, so the library aborts with this explicit error instead of stalling the call.","triggerScenarios":"An agent used on a LiveKit voice call has a tool configured with requireApproval: true, or its execute calls tool suspension (suspend()) awaiting resume; the stream then emits the approval/suspended event during a voice turn.","commonSituations":"Reusing a chat agent (with HITL tools) as a voice agent; adding a 'confirm before action' tool to an agent that also serves voice calls.","solutions":["Remove requireApproval from the tools used by the agent on voice calls.","Refactor the tool to not suspend (execute fully inline) for the voice path, or provide a separate voice-specific agent without HITL tools.","If approval is essential, handle that action outside the voice flow (e.g. a chat channel) rather than via the LiveKit worker."],"exampleFix":"// before\ncreateTool({ id: 'transferFunds', requireApproval: true, execute: ... });\n// after\ncreateTool({ id: 'transferFunds', execute: ... }); // no approval for voice path","handlingStrategy":"validation","validationCode":"function assertNoHitlTools(agent) {\n  for (const tool of agent.getTools()) {\n    if (tool.requireApproval) throw new Error(`Tool ${tool.id} requires approval — unsupported on voice path`);\n  }\n}\nassertNoHitlTools(voiceAgent); // before assigning agent to a voice worker","typeGuard":null,"tryCatchPattern":"try {\n  await runVoiceTurn(ctx);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('tool approval')) {\n    // route the caller to a non-voice channel; do not retry\n    notifyCallerOfChannelFallback();\n  } else throw e;\n}","preventionTips":["Keep voice-serving agents free of requireApproval/suspend-based tools.","Maintain separate agent variants for chat (with HITL) and voice (without).","Add a startup check that walks the voice agent's tools and rejects approval-requiring ones.","Document which shared tools are voice-safe."],"tags":["human-in-the-loop","tools","voice","unsupported-feature","livekit"],"backgroundTag":"unsupported-feature-hitl","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}