{"record":{"id":"b0dc523b8de6aa0d","repo":"actualbudget/actual","slug":"cannot-create-schedules-with-the-same-name","errorCode":null,"errorMessage":"Cannot create schedules with the same name","messagePattern":"Cannot create schedules with the same name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/schedules/app.ts","lineNumber":348,"sourceCode":"\n  const { date: dateCond } = extractScheduleConds(conditions);\n  if (dateCond == null) {\n    throw new Error('A date condition is required to create a schedule');\n  }\n  if (dateCond.value == null) {\n    throw new Error('Date is required');\n  }\n\n  const nextDate = getNextDate(dateCond);\n  const nextDateRepr = nextDate ? toDateRepr(nextDate) : null;\n  const scheduleFields = schedule && {\n    ...schedule,\n    name: normalizeScheduleName(schedule.name),\n  };\n  if (scheduleFields) {\n    if (scheduleFields.name) {\n      if (await checkIfScheduleExists(scheduleFields.name, scheduleId)) {\n        throw new Error('Cannot create schedules with the same name');\n      }\n    }\n  }\n\n  // Create the rule here based on the info\n  const ruleId = await insertRule({\n    stage: null,\n    conditionsOp: 'and',\n    conditions,\n    actions: [{ op: 'link-schedule', value: scheduleId }],\n  });\n\n  const now = Date.now();\n  await db.insertWithUUID('schedules_next_date', {\n    schedule_id: scheduleId,\n    local_next_date: nextDateRepr,\n    local_next_date_ts: now,\n    base_next_date: nextDateRepr,","sourceCodeStart":330,"sourceCodeEnd":366,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/schedules/app.ts#L330-L366","documentation":"`createSchedule` normalizes the schedule name and checks uniqueness via `checkIfScheduleExists`. If another schedule already uses the same (normalized) name, creation is rejected to keep schedule names unique for user clarity.","triggerScenarios":"Calling `createSchedule` with `schedule.name` equal (case/whitespace-insensitively after normalization) to an existing schedule's name, when a schedule payload with a name is provided.","commonSituations":"Automations creating recurring schedules (e.g. 'Rent') that already exist; importing schedules twice; syncing devices that both created the same named schedule.","solutions":["Pick a unique name or append a suffix before calling createSchedule","Check for an existing schedule with the same normalized name first and update it instead","Call `updateSchedule` rather than `createSchedule` when the schedule already exists","List schedules (`getRules`/schedule queries) to find the conflicting name"],"exampleFix":"// before\nawait createSchedule({ name: 'Rent', conditions }); // may already exist\n// after\nconst existing = (await aqlQuery(q('schedules').filter({ name: 'rent' }))).data;\nif (existing.length === 0) {\n  await createSchedule({ name: 'Rent', conditions });\n}","handlingStrategy":"validation","validationCode":"const name = schedule.name.trim().toLowerCase();\nconst dup = (await aqlQuery(q('schedules').filter({ name }))).data;\nif (dup.length > 0) {\n  schedule.name = `${schedule.name} (${new Date().toISOString().slice(0, 10)})`;\n}\nawait createSchedule({ ...schedule, conditions });","typeGuard":null,"tryCatchPattern":"try {\n  await createSchedule({ schedule, conditions });\n} catch (e) {\n  if (e.message === 'Cannot create schedules with the same name') {\n    await createSchedule({ ...schedule, name: schedule.name + ' (2)' }, { conditions });\n  } else {\n    throw e;\n  }\n}","preventionTips":["Check for an existing schedule with the same normalized name before creating","Update existing schedules instead of creating duplicates in import/automation flows","Keep names descriptive and unique (include payee/account in the name)","Make schedule creation idempotent (lookup-then-create or upsert logic)"],"tags":["schedule","duplicate","uniqueness-constraint"],"backgroundTag":"duplicate-schedule-name","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}