mastra-ai/mastra · error · Error
Workflow "${params.id}" declares duplicate schedule id "${en
Error message
Workflow "${params.id}" declares duplicate schedule id "${entry.id}". What it means
When a workflow's `schedule` is an array, ids must be unique across entries; a duplicate id is detected with a `seenIds` Set and a plain `Error` is thrown. Duplicate ids would make it ambiguous which cron/timezone definition a registration refers to, breaking schedule updates and cancellation.
Source
Thrown at packages/core/src/workflows/evented/workflow.ts:1643
any,
any,
any,
EventedEngineType
>[],
TRequestContextSchema extends PublicSchema<any> | undefined = undefined,
>(params: CreateWorkflowParams<TWorkflowId, TStateSchema, TInputSchema, TOutputSchema, TSteps, TRequestContextSchema>) {
if (params.schedule) {
const schedules = Array.isArray(params.schedule) ? params.schedule : [params.schedule];
if (Array.isArray(params.schedule)) {
const seenIds = new Set<string>();
for (const entry of schedules) {
if (!entry.id) {
throw new Error(
`Workflow "${params.id}" declares an array of schedules but one entry is missing the required \`id\` field. Every entry in a schedule array must have a unique stable id.`,
);
}
if (seenIds.has(entry.id)) {
throw new Error(`Workflow "${params.id}" declares duplicate schedule id "${entry.id}".`);
}
seenIds.add(entry.id);
}
}
for (const entry of schedules) {
validateCron(entry.cron, entry.timezone);
}
}
const eventProcessor = new WorkflowEventProcessor({ mastra: params.mastra! });
const executionEngine = new EventedExecutionEngine({
mastra: params.mastra!,
eventProcessor,
options: {
validateInputs: params.options?.validateInputs ?? true,
shouldPersistSnapshot: params.options?.shouldPersistSnapshot ?? (() => true),
pruneSnapshot: params.options?.pruneSnapshot,
tracingPolicy: params.options?.tracingPolicy,
onStart: params.options?.onStart,View on GitHub (pinned to 75dd419e61)
Solutions
- Rename one of the conflicting entries so every id in the array is unique.
- Generate deterministic distinct ids programmatically when building schedules dynamically (e.g. suffix by index or purpose).
- Pre-validate the array with a Set-based uniqueness check before constructing the workflow to fail fast with a clearer message.
Example fix
// before
schedule: [{ id: 'sync', cron: '0 * * * *' }, { id: 'sync', cron: '*/5 * * * *' }]
// after
schedule: [{ id: 'sync-hourly', cron: '0 * * * *' }, { id: 'sync-every-5m', cron: '*/5 * * * *' }] Defensive patterns
Strategy: validation
Validate before calling
function assertUniqueScheduleIds(workflowId: string, schedule: unknown): void {
if (!Array.isArray(schedule)) return;
const seen = new Set<string>();
for (const e of schedule) {
if (seen.has(e.id)) throw new Error(`Workflow "${workflowId}" has duplicate schedule id "${e.id}"`);
seen.add(e.id);
}
}
assertUniqueScheduleIds('report', schedule); Type guard
function idsAreUnique(entries: { id: string }[]): entries is { id: string }[] {
return new Set(entries.map(e => e.id)).size === entries.length;
} Prevention
- Derive schedule ids from purpose (e.g. 'sync-hourly') so collisions are unlikely.
- Run uniqueness checks where schedule arrays are generated from config/env.
- Code-review any copy-pasted schedule entries for unchanged ids.
When it happens
Trigger: `new Workflow({ id, schedule: [ { id: 'a', cron }, { id: 'a', cron } ] })` — the second entry's id is already in `seenIds` (workflow.ts:1641-1649).
Common situations: Copy-pasting a schedule entry and forgetting to change the id; merging schedule lists from config or environment where entries share defaults; template code that clones entries.
Related errors
- Workflow "${params.id}" declares an array of schedules but o
- @mastra/livekit: `workflowInput` is required when `workflow`
- ATOMIC_STORAGE_OPERATIONS_NOT_SUPPORTED
- Google RBAC roleMapping is required.
- Cookie password must be at least 32 characters. Set OKTA_COO
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ca59bcf1b71ca863.
Report an issue: GitHub.