mastra-ai/mastra · error · Error
Schedule ${schedule.id} already exists
Error message
Schedule ${schedule.id} already exists What it means
InMemoryScheduleStorage.createSchedule() throws when a schedule with the same id already exists in the schedules map. Schedule ids are primary keys; the store refuses silent overwrites so callers don't accidentally replace an existing schedule definition.
Source
Thrown at packages/core/src/storage/domains/schedules/inmemory.ts:32
return copy;
}
export class InMemorySchedulesStorage extends SchedulesStorage {
private db: InMemoryDB;
constructor({ db }: { db: InMemoryDB }) {
super();
this.db = db;
}
async dangerouslyClearAll(): Promise<void> {
this.db.schedules.clear();
this.db.scheduleTriggers.length = 0;
}
async createSchedule(schedule: Schedule): Promise<Schedule> {
if (this.db.schedules.has(schedule.id)) {
throw new Error(`Schedule ${schedule.id} already exists`);
}
const stored = clone(schedule);
this.db.schedules.set(stored.id, stored);
return clone(stored);
}
async getSchedule(id: string): Promise<Schedule | null> {
const found = this.db.schedules.get(id);
return found ? cloneRow(found) : null;
}
async listSchedules(filter?: ScheduleFilter): Promise<Schedule[]> {
let rows = Array.from(this.db.schedules.values());
if (filter?.status) {
rows = rows.filter(r => r.status === filter.status);
}
if (filter?.workflowId) {
rows = rows.filter(r => r.target.type === 'workflow' && r.target.workflowId === filter.workflowId);View on GitHub (pinned to 75dd419e61)
Solutions
- Generate unique ids per schedule (crypto.randomUUID()) instead of fixed strings.
- Check existence first (listSchedules / getSchedule) and skip or update instead of create.
- On restart, upsert: catch this error and call updateSchedule(id, patch) as a fallback.
- Call clearSchedules() (or use a fresh storage instance) between test runs.
Example fix
// before
await storage.schedules.createSchedule({ id: 'nightly-job', ... });
// after
const id = crypto.randomUUID();
await storage.schedules.createSchedule({ id, ...scheduleDef }); Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await storage.schedules.listSchedules();
if (existing.schedules?.some(s => s.id === schedule.id)) {
throw new Error(`schedule ${schedule.id} already registered`);
} Type guard
function scheduleExists(schedules: { id: string }[], id: string): boolean {
return schedules.some(s => s.id === id);
} Try / catch
try {
await storage.schedules.createSchedule(schedule);
} catch (e) {
if (e instanceof Error && /^Schedule .+ already exists$/.test(e.message)) {
await storage.schedules.updateSchedule(schedule.id, { ...schedule });
return;
}
throw e;
} Prevention
- Use crypto.randomUUID() for schedule ids instead of fixed strings.
- Make startup registration idempotent: check-then-create or catch-and-update.
- Clear in-memory schedules between test runs (clearSchedules / fresh instance).
- Keep schedule id constants in one place to avoid accidental duplicates.
When it happens
Trigger: Calling createSchedule({ id: 'sched-1', ... }) twice with the same id; re-running a registration/startup routine that creates schedules on every boot; a makeDue/makeDue test helper creating a schedule whose id collides with a pre-seeded one.
Common situations: App restart re-registering cron-like schedules into a still-populated in-memory store; duplicated constants for schedule ids; parallel test suites sharing one storage instance.
Related errors
- Version number ${input.versionNumber} already exists for pro
- Schedule ${id} not found
- Scorer definition with id ${scorerDefinition.id} already exi
- Version with id ${input.id} already exists
- Skill with id ${skill.id} already exists
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ff0bfd89256b3677.
Report an issue: GitHub.