mastra-ai/mastra · error · Error

Schedule 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.

Solutions

  1. Generate unique ids per schedule (crypto.randomUUID()) instead of fixed strings.
  2. Check existence first (listSchedules / getSchedule) and skip or update instead of create.
  3. On restart, upsert: catch this error and call updateSchedule(id, patch) as a fallback.
  4. 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

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


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

Appendix: 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)