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

  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.