mastra-ai/mastra · error

@mastra/livekit: reply generation runs through the Mastra ag

Error message

@mastra/livekit: reply generation runs through the Mastra agent via llmNode; the placeholder LLM cannot be used for inference.

What it means

The placeholder LLM shipped inside MastraVoiceAgent implements chat() solely to satisfy the LiveKit Agent interface — it never performs inference. If code calls chat() directly (e.g. bypassing the agent's llmNode pipeline), the bridge throws this error to make it explicit that reply generation must flow through the Mastra agent via llmNode.

Source

Thrown at integrations/livekit/src/bridge.ts:415

 * The session only runs its cascaded reply pipeline when an `llm` instance is present —
 * `llmNode` replaces the inference step, but the gate checks `llm instanceof LLM`. This
 * placeholder satisfies the gate; the Mastra agent/workflow does the actual generation.
 */
class MastraPlaceholderLLM extends llm.LLM {
  label(): string {
    return 'mastra.MastraVoiceAgent';
  }

  override get model(): string {
    return 'mastra-agent';
  }

  override get provider(): string {
    return 'mastra';
  }

  chat(): llm.LLMStream {
    throw new Error(
      '@mastra/livekit: reply generation runs through the Mastra agent via llmNode; the placeholder LLM cannot be used for inference.',
    );
  }
}

/**
 * A LiveKit `voice.Agent` whose replies come from a Mastra agent or workflow.
 *
 * LiveKit keeps ownership of the audio loop (VAD, STT, turn detection, TTS, barge-in) and calls
 * `llmNode` once per detected user turn; the node delegates to a {@link VoiceReplyGenerator}
 * which streams text deltas back. On barge-in LiveKit cancels the returned stream, which aborts
 * the in-flight generation.
 */
export class MastraVoiceAgent extends voice.Agent {
  readonly mastraAgent?: MastraAgent;
  readonly memory: MastraVoiceAgentMemory | false;
  readonly requestContext?: RequestContext;
  readonly streamOptions?: MastraStreamOptions;

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Do not call chat() on the agent's LLM; rely on llmNode which routes turns through the Mastra agent.
  2. Construct MastraVoiceAgent with a valid `agent` (Mastra agent instance) or `generate` function so llmNode has a real reply source.
  3. If you need manual reply generation, use the `generate` option instead of invoking the LLM directly.
  4. Remove or replace any custom pipeline code that calls the LLM's chat() method directly.

Example fix

// before
const stream = await voiceAgent.llm.chat(chatCtx); // throws
// after
const stream = voiceAgent.llmNode(chatCtx, toolCtx); // routed through the Mastra agent
Defensive patterns

Strategy: try-catch

Try / catch

// never call voiceAgent.llm.chat() directly; if wrapping the pipeline:
try {
  const stream = voiceAgent.llmNode(chatCtx, toolCtx);
} catch (err) {
  if (err instanceof Error && err.message.includes('placeholder LLM cannot be used')) {
    throw new Error('Misconfigured pipeline: route replies through llmNode with a real agent/generate');
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling chat() on a MastraVoiceAgent (or its placeholder LLM) directly, or configuring a LiveKit pipeline component that invokes the LLM's chat() instead of the agent's llmNode — e.g. custom turn-detection or manual session code driving the LLM itself.

Common situations: Porting plain LiveKit agent code that called this.llm.chat(...) manually; wiring the MastraVoiceAgent into a pipeline expecting a vanilla LLM; constructing MastraVoiceAgent without agent/generate and then trying to use its LLM.

Related errors


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