{"record":{"id":"fc7cd992ed31d5c2","repo":"actualbudget/actual","slug":"no-rule-found-for-schedule","errorCode":null,"errorMessage":"No rule found for schedule","messagePattern":"No rule found for schedule","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/schedules/app.ts","lineNumber":227,"sourceCode":"}\n\nexport async function setNextDate({\n  id,\n  start,\n  conditions,\n  reset,\n  skipRequested,\n}: {\n  id: string;\n  start?;\n  conditions?;\n  reset?: boolean;\n  skipRequested?: boolean;\n}) {\n  if (conditions == null) {\n    const rule = await getRuleForSchedule(id);\n    if (rule == null) {\n      throw new Error('No rule found for schedule');\n    }\n    conditions = rule.serialize().conditions;\n  }\n\n  const { date: dateCond } = extractScheduleConds(conditions);\n\n  let { data: nextDate } = await aqlQuery(\n    q('schedules').filter({ id }).calculate('next_date'),\n  );\n\n  if (skipRequested === true) {\n    const skipWeekend: boolean = dateCond.value?.skipWeekend;\n    const weekendSolveMode: string = dateCond.value?.weekendSolveMode;\n\n    if (weekendSolveMode === 'before' && skipWeekend === true) {\n      const parsedNextDate = parseDate(nextDate);\n      if (d.isFriday(parsedNextDate) || d.isWeekend(parsedNextDate)) {\n        // nextDate is on weekend or friday, moving to monday","sourceCodeStart":209,"sourceCodeEnd":245,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/schedules/app.ts#L209-L245","documentation":"`setNextDate` looks up the schedule's rule only when `conditions` were not supplied; if the resulting rule is null (rule id points to a deleted/missing rule) it throws 'No rule found for schedule'. This guards against computing the next date from nonexistent conditions.","triggerScenarios":"Calling `setNextDate(id, ...)` without `conditions` when the schedule's backing rule no longer exists (deleted rule, stale rule id), via updateSchedule, skipNextDate, or the schedules advancement service.","commonSituations":"Rules deleted out-of-band while schedules remained; sync conflicts removing the rule but not the schedule; database restored partially.","solutions":["Pass explicit `conditions` containing a valid date condition to `setNextDate` so the rule lookup is skipped","Check the schedules table `rule` column references an existing rule id","Recreate the rule for the schedule or delete and recreate the schedule","Pass `reset: true` with conditions to reinitialize the schedule"],"exampleFix":"// before\nawait setNextDate(scheduleId, { reset: true }); // rule missing -> throws\n// after\nawait setNextDate(scheduleId, {\n  reset: true,\n  conditions: [\n    { field: 'date', op: 'isapprox', value: { frequency: 'monthly', start: '2026-01-01' } },\n  ],\n});","handlingStrategy":"validation","validationCode":"const ruleId = (await aqlQuery(q('schedules').filter({ id }).calculate('rule'))).data;\nif (!conditions && ruleId == null) {\n  throw new Error('Cannot setNextDate: schedule has no rule; supply conditions explicitly');\n}\nawait setNextDate(id, { conditions: dateConditions });","typeGuard":"function isRuleWithConditions(rule) {\n  return rule != null &&\n    Array.isArray(rule.serialize().conditions) &&\n    rule.serialize().conditions.some(c => c.field === 'date');\n}","tryCatchPattern":"try {\n  await setNextDate(id, { reset: true });\n} catch (e) {\n  if (e.message === 'No rule found for schedule') {\n    logger.warn('Orphaned schedule', id, '— recreating rule');\n    await recreateScheduleRule(id);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Pass explicit conditions when the schedule's rule may be missing","Verify schedules.rule references an existing rule before schedule maintenance APIs","Delete schedules together with their rules to avoid orphans","Run periodic integrity checks for schedules with null or dangling rule ids"],"tags":["schedule","data-integrity","missing-record"],"backgroundTag":"schedule-missing-rule","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}