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
- Remove the 'prompt' field if you want the schedule to run the handler function.
- Remove the 'handler' field (set it to undefined) if you want the schedule to send the prompt to the agent.
- If both came from a merge/defaults spread, explicitly delete the unwanted key (e.g. `delete merged.prompt`) before calling defineSchedule.
- 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
- Decide on one execution mode per schedule and never set both fields in shared defaults.
- When merging config objects, explicitly delete the excluded key instead of spreading both sources.
- Use defineSchedule() at author time so TypeScript and runtime validation catch the conflict immediately.
- Add a lint/test that scans schedule definitions for both fields before deploy.
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
- SCHEDULES_MISSING_MODE
- SCHEDULES_INVALID_DEFINITION
- SCHEDULES_INVALID_CRON
- SCHEDULES_INVALID_SIGNAL_TYPE
- SCHEDULES_INVALID_STATUS
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/08c1cea7721e5448.
Report an issue: GitHub.