mastra-ai/mastra · error

@mastra/livekit: `workflowInput` is required when `workflow`

Error message

@mastra/livekit: `workflowInput` is required when `workflow` is set. Map the turn into the workflow inputData, e.g. workflowInput: ({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) }).

What it means

When `workflow` is the reply generator, the worker needs a `workflowInput` mapper to turn the LiveKit chat context into the workflow's inputData schema; without it the workflow would receive no input and fail. The library therefore requires `workflowInput` whenever `workflow` is set.

Source

Thrown at integrations/livekit/src/worker.ts:793

 *
 * if (process.argv[1] === fileURLToPath(import.meta.url)) {
 *   runLiveKitWorker({ entry: import.meta.url, agentName: 'mastra-voice' });
 * }
 * ```
 */
export function createLiveKitWorker(options: CreateLiveKitWorkerOptions) {
  if (options.generate && (options.agent || options.workflow)) {
    throw new Error(
      '@mastra/livekit: set exactly one reply generator — `generate`, `agent`, or `workflow` — not a combination.',
    );
  }
  if (options.agent && options.workflow) {
    throw new Error(
      '@mastra/livekit: set `agent` or `workflow`, not both — they are mutually exclusive reply generators.',
    );
  }
  if (options.workflow && !options.workflowInput) {
    throw new Error(
      '@mastra/livekit: `workflowInput` is required when `workflow` is set. Map the turn into the ' +
        'workflow inputData, e.g. workflowInput: ({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) }).',
    );
  }
  if (options.generate && options.configuration?.endCall) {
    throw new Error(
      '@mastra/livekit: `configuration.endCall` has no effect with `generate` — the worker cannot observe ' +
        'tool calls from a custom reply generator. Detect the end-call tool inside your generator and call ' +
        '`runEndCall` directly instead.',
    );
  }

  const wantsSileroVad = options.vad === undefined || options.vad === 'silero';

  // The turn detector's inference runners register at plugin-import time, and the agent
  // server only spawns its inference process for runners registered before it starts —
  // so begin the import now (worker definition happens at module scope, before
  // runLiveKitWorker boots the server, which awaits this).

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Add a workflowInput function that maps ({ chatCtx }) to the workflow inputData, e.g. ({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) }).
  2. Match the returned object to the workflow's zod input schema field names exactly.

Example fix

// before
createLiveKitWorker({ workflow: myWorkflow });
// after
createLiveKitWorker({
  workflow: myWorkflow,
  workflowInput: ({ chatCtx }) => ({ history: chatContextToMessages(chatCtx) }),
});
Defensive patterns

Strategy: validation

Validate before calling

if (options.workflow && !options.workflowInput) throw new Error('workflowInput is required when workflow is set');

Type guard

function workflowOptionsValid(o: { workflow?: unknown; workflowInput?: unknown }): boolean {
  return o.workflow == null || typeof o.workflowInput === 'function';
}

Try / catch

try {
  const worker = createLiveKitWorker(options);
} catch (e) {
  if ((e as Error).message.includes('workflowInput is required')) {
    console.error('Add workflowInput: ({ chatCtx }) => ({ ... }) matching your workflow input schema');
  } else throw e;
}

Prevention

When it happens

Trigger: createLiveKitWorker({ workflow: myWorkflow }) with options.workflowInput undefined — the guard `options.workflow && !options.workflowInput` fires.

Common situations: Building a workflow-based voice agent and missing the required companion option; converting a sample that used `agent` (no input mapper needed) to `workflow`; TypeScript types bypassed via `as` casts or loose typing.

Related errors


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