{"record":{"id":"fc58c979a9821c6a","repo":"mastra-ai/mastra","slug":"mastra-livekit-mastravoiceagent-requires-agent","errorCode":null,"errorMessage":"@mastra/livekit: MastraVoiceAgent requires `agent` or `generate`, not both — they are mutually exclusive reply sources.","messagePattern":"@mastra/livekit: MastraVoiceAgent requires `agent` or `generate`, not both — they are mutually exclusive reply sources\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integrations/livekit/src/bridge.ts","lineNumber":439,"sourceCode":"/**\n * A LiveKit `voice.Agent` whose replies come from a Mastra agent or workflow.\n *\n * LiveKit keeps ownership of the audio loop (VAD, STT, turn detection, TTS, barge-in) and calls\n * `llmNode` once per detected user turn; the node delegates to a {@link VoiceReplyGenerator}\n * which streams text deltas back. On barge-in LiveKit cancels the returned stream, which aborts\n * the in-flight generation.\n */\nexport class MastraVoiceAgent extends voice.Agent {\n  readonly mastraAgent?: MastraAgent;\n  readonly memory: MastraVoiceAgentMemory | false;\n  readonly requestContext?: RequestContext;\n  readonly streamOptions?: MastraStreamOptions;\n  private readonly replyGenerator: VoiceReplyGenerator;\n  private readonly reminder?: DisclosureReminder;\n\n  constructor(options: MastraVoiceAgentOptions) {\n    if (options.agent && options.generate) {\n      throw new Error(\n        '@mastra/livekit: MastraVoiceAgent requires `agent` or `generate`, not both — they are mutually exclusive reply sources.',\n      );\n    }\n    super({\n      id: options.id,\n      instructions: options.instructions ?? DEFAULT_INSTRUCTIONS,\n      stt: options.stt,\n      vad: options.vad,\n      llm: new MastraPlaceholderLLM(),\n      tts: options.tts,\n      turnHandling: options.turnHandling,\n    });\n    this.memory = options.memory ?? false;\n    this.requestContext = toRequestContext(options.requestContext);\n    this.streamOptions = options.streamOptions;\n    if (options.greetingReminder) {\n      this.reminder = new DisclosureReminder(\n        options.greetingReminder.everyMs,","sourceCodeStart":421,"sourceCodeEnd":457,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/integrations/livekit/src/bridge.ts#L421-L457","documentation":"MastraVoiceAgent accepts two mutually exclusive reply sources: `agent` (a Mastra agent instance) or `generate` (a custom reply generator function). The constructor throws this error when both are provided, because the bridge cannot decide which one owns reply generation. Exactly one must be supplied.","triggerScenarios":"new MastraVoiceAgent({ agent, generate, ... }) where both keys are present — typically when merging config objects or adding a custom generate override on top of an existing agent-based config.","commonSituations":"Spreading defaults ({ ...baseOptions }) that already contain `agent` while adding `generate`; refactoring from agent-based to generate-based replies without removing the old key; conditional config building that accumulates both fields.","solutions":["Remove one of the two: keep `agent` for Mastra-agent-driven replies, or keep `generate` for custom replies.","If migrating from agent to generate, delete the `agent` property from the options object.","Audit config-building code (object spreads, defaults) so only one reply source is ever set."],"exampleFix":"// before\nnew MastraVoiceAgent({ agent: myAgent, generate: myFn })\n// after\nnew MastraVoiceAgent({ agent: myAgent }) // or { generate: myFn }, never both","handlingStrategy":"validation","validationCode":"function assertValidVoiceAgentOptions(o: MastraVoiceAgentOptions): void {\n  if ('agent' in o && o.agent && 'generate' in o && o.generate) {\n    throw new Error('MastraVoiceAgent accepts `agent` or `generate`, not both');\n  }\n}","typeGuard":"function hasExactlyOneReplySource(o: MastraVoiceAgentOptions): boolean {\n  return Boolean(o.agent) !== Boolean(o.generate); // XOR: exactly one present\n}","tryCatchPattern":null,"preventionTips":["Build options objects explicitly instead of spreading partial configs that may carry both keys.","Centralize MastraVoiceAgent construction in one factory that enforces the XOR constraint.","When migrating from agent to generate, delete the old key, don't just add the new one."],"tags":["validation","api-misuse","constructor","livekit"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}