{"record":{"id":"cc231db75d7dfacd","repo":"actualbudget/actual","slug":"parse-recur-date","errorCode":"parse-recur-date","errorMessage":"parse-recur-date: ${e.message}","messagePattern":"parse-recur-date: (.+?)","errorType":"error_code","errorClass":"RuleError","httpStatus":null,"severity":"error","filePath":"packages/loot-core/src/server/rules/rule-utils.ts","lineNumber":235,"sourceCode":"  }\n}\n\nexport function parseRecurDate(desc) {\n  try {\n    const rules = recurConfigToRSchedule(desc);\n\n    return {\n      type: 'recur',\n      schedule: new RSchedule({\n        rrules: rules,\n        data: {\n          skipWeekend: desc.skipWeekend,\n          weekendSolve: desc.weekendSolveMode,\n        },\n      }),\n    };\n  } catch (e) {\n    throw new RuleError('parse-recur-date', e.message);\n  }\n}\n\nexport function parseDateString(str) {\n  if (typeof str !== 'string') {\n    return null;\n  } else if (str.length === 10) {\n    // YYYY-MM-DD\n    if (!dateFns.isValid(dateFns.parseISO(str))) {\n      return null;\n    }\n\n    return { type: 'date', date: str };\n  } else if (str.length === 7) {\n    // YYYY-MM\n    if (!dateFns.isValid(dateFns.parseISO(str + '-01'))) {\n      return null;\n    }","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/actualbudget/actual/blob/d4334cb6e6123f4d3bcea1ad6166608884c7e658/packages/loot-core/src/server/rules/rule-utils.ts#L217-L253","documentation":"`parseRecurDate` wraps any exception raised while parsing a recurring-date descriptor into a `RuleError` with code `parse-recur-date`. Recurring schedule date strings (RRULE-like objects) must conform to the expected descriptor shape; invalid frequency, interval, or sub-date values throw here.","triggerScenarios":"Passing a malformed recurring date object to a date condition (e.g. `frequency` misspelled, missing `start`, invalid `interval`, bad nested `between`/`on` descriptors) which the underlying parser throws on.","commonSituations":"Hand-authoring recurring schedule conditions via the API; importing schedules from another tool; edited budget data where the `date` condition value was mangled.","solutions":["Inspect `e.message` embedded in the error — it is the underlying parser message","Validate the recurring descriptor: `frequency` in {daily, weekly, monthly, yearly}, positive `interval`, valid `start` date string","Build schedules through the UI or a schedule-creation helper instead of hand-writing descriptors","Fix the offending condition value in the schedule/rule data"],"exampleFix":"// before\n{ field: 'date', op: 'isapprox', value: { frequency: 'biweekly', start: '2026-01-01' } }\n// after\n{ field: 'date', op: 'isapprox', value: { frequency: 'weekly', interval: 2, start: '2026-01-01' } }","handlingStrategy":"validation","validationCode":"function isValidRecurDescriptor(v) {\n  const freqs = ['daily', 'weekly', 'monthly', 'yearly'];\n  return (\n    v && typeof v === 'object' &&\n    freqs.includes(v.frequency) &&\n    (v.interval == null || (Number.isInteger(v.interval) && v.interval > 0)) &&\n    typeof v.start === 'string' && /^\\d{4}-\\d{2}-\\d{2}$/.test(v.start)\n  );\n}","typeGuard":"function isRecurringDateValue(v) {\n  return typeof v === 'object' && v !== null &&\n    typeof (v).frequency === 'string' &&\n    typeof (v).start === 'string';\n}","tryCatchPattern":"try {\n  const next = parseRecurDate(desc);\n} catch (e) {\n  if (e.type === 'parse-recur-date') {\n    logger.error('Bad recurring descriptor', desc, e.message);\n  } else {\n    throw e;\n  }\n}","preventionTips":["Validate recurring descriptors against the expected schema (frequency, interval, start) before use","Use the UI or shared schedule helpers to build descriptors, never hand-write them","Include the raw descriptor in error logs to diagnose parse failures","Test descriptors with a known-good sample (e.g. {frequency:'monthly', start:'2026-01-01'})"],"tags":["schedule","date-parsing","recurring-date"],"backgroundTag":"invalid-recurring-date-descriptor","analyzedSha":"d4334cb6e6123f4d3bcea1ad6166608884c7e658","analyzedAt":"2026-08-29T01:02:11.213Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}