mastra-ai/mastra · error · MastraError

SCHEDULES_AMBIGUOUS_MODE

SCHEDULES_AMBIGUOUS_MODE

Error message

Schedule${where}: set exactly one execution mode — remove either 'prompt' or 'handler'.

What it means

A schedule definition passed both a 'prompt' and a 'handler', which are mutually exclusive execution modes: a schedule runs either by sending a prompt to an agent or by calling a handler function, never both. Mastra throws this at definition time (via defineSchedule or file-based assembly through assertValidScheduleDefinition) so the ambiguity is caught before the schedule is stored or fired. The error text includes the schedule label when one is known.

Source

Thrown at packages/core/src/schedules/define.ts:168

 */
export function assertValidScheduleDefinition(definition: AgentScheduleDefinition<any>, label?: string): void {
  const where = label ? ` (${label})` : '';

  if (!definition || typeof definition !== 'object') {
    throw new MastraError({
      id: 'SCHEDULES_INVALID_DEFINITION',
      domain: ErrorDomain.AGENT,
      category: ErrorCategory.USER,
      details: { label: label ?? '' },
      text: `Schedule${where}: expected a schedule definition object, received ${definition === null ? 'null' : typeof definition}.`,
    });
  }

  const hasPrompt = typeof definition.prompt === 'string' && definition.prompt.trim() !== '';
  const hasHandler = typeof definition.handler === 'function';

  if (hasPrompt && hasHandler) {
    throw new MastraError({
      id: 'SCHEDULES_AMBIGUOUS_MODE',
      domain: ErrorDomain.AGENT,
      category: ErrorCategory.USER,
      details: { label: label ?? '' },
      text: `Schedule${where}: set exactly one execution mode — remove either 'prompt' or 'handler'.`,
    });
  }

  if (!hasPrompt && !hasHandler) {
    throw new MastraError({
      id: 'SCHEDULES_MISSING_MODE',
      domain: ErrorDomain.AGENT,
      category: ErrorCategory.USER,
      details: { label: label ?? '' },
      text: `Schedule${where}: set exactly one execution mode — provide a non-empty 'prompt' or a 'handler' function.`,
    });
  }

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Remove the 'prompt' field if you want the schedule to run the handler function.
  2. Remove the 'handler' field (set it to undefined) if you want the schedule to send the prompt to the agent.
  3. If both came from a merge/defaults spread, explicitly delete the unwanted key (e.g. `delete merged.prompt`) before calling defineSchedule.
  4. Re-run or rebuild so assertValidScheduleDefinition passes and the schedule registers.

Example fix

// before
defineSchedule({
  cron: '0 9 * * *',
  prompt: 'Check system health.',
  handler: async ({ agent }) => { /* ... */ },
});
// after
defineSchedule({
  cron: '0 9 * * *',
  handler: async ({ agent }) => { /* ... */ },
});
Defensive patterns

Strategy: validation

Validate before calling

function hasExecMode(def) {
  const hasPrompt = typeof def?.prompt === 'string' && def.prompt.trim() !== '';
  const hasHandler = typeof def?.handler === 'function';
  return hasPrompt !== hasHandler; // exactly one
}
if (!hasExecMode(mySchedule)) throw new Error('Set exactly one of prompt or handler');

Type guard

function hasExactlyOneMode(def: { prompt?: string; handler?: unknown }): boolean {
  const hasPrompt = typeof def.prompt === 'string' && def.prompt.trim() !== '';
  const hasHandler = typeof def.handler === 'function';
  return hasPrompt !== hasHandler;
}

Try / catch

try {
  const schedule = defineSchedule(def);
} catch (e) {
  if (e instanceof MastraError && e.id === 'SCHEDULES_AMBIGUOUS_MODE') {
    // drop one of prompt/handler and retry, or surface a config error
  } else throw e;
}

Prevention

When it happens

Trigger: Calling defineSchedule({cron, prompt, handler}) with both a non-empty prompt string and a handler function; passing an assembled/markdown schedule object to resolveSchedules/assertValidScheduleDefinition where both fields ended up set (e.g. merging defaults with a user definition).

Common situations: Copying an example that used 'prompt' into code that already had a 'handler'; a config merge spreading base defaults containing 'prompt' over an object that defines 'handler'; refactoring from prompt-mode to handler-mode and leaving the old prompt field in place; TypeScript not catching it because markdown/file-based schedules bypass type checking.

Related errors


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