{"record":{"id":"495f70185b939847","repo":"hcengineering/platform","slug":"invalid-recurring-rule-frequency","errorCode":null,"errorMessage":"Invalid recurring rule frequency","messagePattern":"Invalid recurring rule frequency","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/calendar/src/utils.ts","lineNumber":44,"sourceCode":"\nexport function generateRecurringValues (\n  rule: RecurringRule,\n  startDate: Timestamp,\n  from: Timestamp,\n  to: Timestamp\n): Timestamp[] {\n  const currentDate = new Date(startDate)\n  switch (rule.freq) {\n    case 'DAILY':\n      return generateDailyValues(rule, currentDate, from, to)\n    case 'WEEKLY':\n      return generateWeeklyValues(rule, currentDate, from, to)\n    case 'MONTHLY':\n      return generateMonthlyValues(rule, currentDate, from, to)\n    case 'YEARLY':\n      return generateYearlyValues(rule, currentDate, from, to)\n    default:\n      throw new Error('Invalid recurring rule frequency')\n  }\n}\n\nfunction generateDailyValues (rule: RecurringRule, currentDate: Date, from: Timestamp, to: Timestamp): Timestamp[] {\n  const values: Timestamp[] = []\n  const { count, endDate, interval } = rule\n  const { bySetPos } = rule\n  let i = 0\n\n  while (true) {\n    if (bySetPos == null || bySetPos.includes(getSetPos(currentDate))) {\n      const res = currentDate.getTime()\n      if (currentDate.getTime() > to) break\n      if (endDate != null && currentDate.getTime() > endDate) break\n      if (res >= from && res <= to) {\n        values.push(res)\n      }\n      i++","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/plugins/calendar/src/utils.ts#L26-L62","documentation":"generateRecurringValues switches on the recurrence rule's frequency (DAILY, WEEKLY, MONTHLY, YEARLY). Any other frequency value falls through to default and throws, since no expansion strategy exists for it.","triggerScenarios":"A calendar event has a RecurringRule whose freq property is not one of the four supported uppercase values — e.g. lowercase 'daily', a typo, or an unsupported iCalendar FREQ like HOURLY/SECONDLY stored in the rule.","commonSituations":"Importing iCalendar files from other systems with exotic frequencies; manual rule construction with wrong casing; older/newer data versions with frequencies this build does not support.","solutions":["Normalize rule.frequency to an uppercase supported value (DAILY/WEEKLY/MONTHLY/YEARLY) before saving","Map unsupported frequencies (HOURLY etc.) to the closest supported rule at import time","Validate recurrence rules on input with a whitelist of allowed frequencies"],"exampleFix":"// before\nconst rule = { freq: 'daily', ... } // lowercase, throws\n// after\nconst rule = { freq: String(raw.freq).toUpperCase() as 'DAILY'|'WEEKLY'|'MONTHLY'|'YEARLY', ... }","handlingStrategy":"validation","validationCode":"const SUPPORTED = ['DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'] as const\nif (!SUPPORTED.includes(rule.freq.toUpperCase() as any)) {\n  throw new Error(`Unsupported frequency: ${rule.freq}`)\n}","typeGuard":"const isSupportedFrequency = (f: string): f is 'DAILY'|'WEEKLY'|'MONTHLY'|'YEARLY' =>\n  ['DAILY', 'WEEKLY', 'MONTHLY', 'YEARLY'].includes(f.toUpperCase())","tryCatchPattern":"try {\n  values = generateRecurringValues(rule, currentDate, from, to)\n} catch (err) {\n  if ((err as Error).message === 'Invalid recurring rule frequency') {\n    values = [] // treat as non-recurring or normalize rule\n  } else throw err\n}","preventionTips":["Uppercase and validate freq at rule creation/import time","Whitelist frequencies in forms and iCalendar importers","Add unit tests covering all four supported frequencies plus rejects"],"tags":["calendar","validation","recurrence"],"backgroundTag":"invalid-recurring-rule-frequency","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}