{"record":{"id":"b2cc1c44ddeb2be8","repo":"phacility/phabricator","slug":"rrule-specifies-bymonthday-with-freq-set-to-weekly","errorCode":null,"errorMessage":"RRULE specifies BYMONTHDAY with FREQ set to WEEKLY, which violates RFC5545.","messagePattern":"RRULE specifies BYMONTHDAY with FREQ set to WEEKLY, which violates RFC5545\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php","lineNumber":574,"sourceCode":"    self::getWeekdayIndex($week_start);\n\n    $this->weekStart = $week_start;\n    return $this;\n  }\n\n  public function getWeekStart() {\n    return $this->weekStart;\n  }\n\n  public function resetSource() {\n    $frequency = $this->getFrequency();\n\n    if ($this->getByMonthDay()) {\n      switch ($frequency) {\n        case self::FREQUENCY_WEEKLY:\n          // RFC5545: \"The BYMONTHDAY rule part MUST NOT be specified when the\n          // FREQ rule part is set to WEEKLY.\"\n          throw new Exception(\n            pht(\n              'RRULE specifies BYMONTHDAY with FREQ set to WEEKLY, which '.\n              'violates RFC5545.'));\n          break;\n        default:\n          break;\n      }\n\n    }\n\n    if ($this->getByYearDay()) {\n      switch ($frequency) {\n        case self::FREQUENCY_DAILY:\n        case self::FREQUENCY_WEEKLY:\n        case self::FREQUENCY_MONTHLY:\n          // RFC5545: \"The BYYEARDAY rule part MUST NOT be specified when the\n          // FREQ rule part is set to DAILY, WEEKLY, or MONTHLY.\"\n          throw new Exception(","sourceCodeStart":556,"sourceCodeEnd":592,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php#L556-L592","documentation":"RFC5545 states that BYMONTHDAY MUST NOT be combined with FREQ=WEEKLY, because 'day of month' has no meaning inside a weekly cycle. PhutilCalendarRecurrenceRule::resetSource() enforces this as soon as the rule starts evaluating, so any weekly rule carrying BYMONTHDAY fails fast instead of producing wrong dates.","triggerScenarios":"An RRULE like 'FREQ=WEEKLY;BYMONTHDAY=15', or building the rule in code via setFrequency(self::FREQUENCY_WEEKLY) followed by setByMonthDay(array(15)) and then calling getEvents()/resetSource().","commonSituations":"Machine-generated or hand-written .ics files that reuse BYMONTHDAY across frequencies; code that copies a monthly rule template and only changes FREQ to WEEKLY; calendar imports from buggy producers.","solutions":["Remove the BYMONTHDAY part if a plain weekly recurrence is intended","Switch FREQ to MONTHLY or DAILY if the day-of-month filter is actually required","Pre-validate imported RRULEs: reject BYMONTHDAY whenever FREQ=WEEKLY before handing the rule to the parser"],"exampleFix":"// before\n$rrule = 'FREQ=WEEKLY;BYMONTHDAY=15;BYDAY=MO';\n\n// after\n$rrule = 'FREQ=WEEKLY;BYDAY=MO';","handlingStrategy":"validation","validationCode":"$parts = array();\nforeach (explode(';', $rrule) as $part) {\n  $kv = explode('=', $part, 2);\n  $parts[$kv[0]] = idx($kv, 1);\n}\nif (idx($parts, 'FREQ') === 'WEEKLY' && isset($parts['BYMONTHDAY'])) {\n  throw new Exception('BYMONTHDAY is not allowed with FREQ=WEEKLY (RFC5545).');\n}","typeGuard":null,"tryCatchPattern":"try {\n  $rule->getEvents();\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'BYMONTHDAY') !== false) {\n    // Strip BYMONTHDAY and retry with a sanitized rule.\n  }\n  throw $ex;\n}","preventionTips":["Treat RFC5545 MUST NOT combinations as import-time rejections, not runtime surprises","When generating rules from templates, derive allowed parts from FREQ instead of reusing a fixed part list","Unit-test each generated rule shape against the parser before storing it"],"tags":["rrule","icalendar","rfc5545","calendar","bymonthday"],"backgroundTag":"rrule-validation-failed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}