{"record":{"id":"3794160dc8a6e848","repo":"phacility/phabricator","slug":"value-s-in-rrule-s-parameter-is-invalid-it-379416","errorCode":null,"errorMessage":"Value \"%s\" in RRULE \"%s\" parameter is invalid: it must not be zero.","messagePattern":"Value \"(.+?)\" in RRULE \"(.+?)\" parameter is invalid: it must not be zero\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php","lineNumber":1607,"sourceCode":"            'Value \"%s\" in RRULE \"%s\" parameter is invalid: values must be '.\n            'integers.',\n            $value,\n            $source));\n      }\n\n      if ($value < $min || $value > $max) {\n        throw new Exception(\n          pht(\n            'Value \"%s\" in RRULE \"%s\" parameter is invalid: it must be '.\n            'between %s and %s.',\n            $value,\n            $source,\n            $min,\n            $max));\n      }\n\n      if (!$value && !$allow_zero) {\n        throw new Exception(\n          pht(\n            'Value \"%s\" in RRULE \"%s\" parameter is invalid: it must not '.\n            'be zero.',\n            $value,\n            $source));\n      }\n    }\n  }\n\n  private function getSetPositionState() {\n    $scale = $this->getFrequencyScale();\n\n    $parts = array();\n    $parts[] = $this->stateYear;\n\n    if ($scale == self::SCALE_WEEKLY) {\n      $parts[] = $this->stateWeek;\n    } else {","sourceCodeStart":1589,"sourceCodeEnd":1625,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php#L1589-L1625","documentation":"Some RRULE parameters disallow zero: BYMONTHDAY=0 and BYYEARDAY=0 are illegal under RFC5545 (there is no 'day zero'; negatives count from the end). assertByRange() is called with allow_zero=false for those parameters and throws on a zero value even though zero is in the numeric range.","triggerScenarios":"An RRULE containing 'BYMONTHDAY=0' or 'BYYEARDAY=0', or programmatic setByMonthDay(array(0)) / setByYearDay(array(0)) on a rule that is then validated.","commonSituations":"Calendar UIs offering '0' as a selectable day; date math that subtracts into zero (e.g. computing day offsets with bc math or negative modulo bugs); imports from tools that normalize missing days to zero.","solutions":["Remove the zero value from the list","If 'last day' was intended, use -1 instead of 0","Sanitize BYMONTHDAY/BYYEARDAY inputs with array_filter($v, function($x) { return $x !== 0; }) before setting"],"exampleFix":"// before\n$rrule = 'FREQ=MONTHLY;BYMONTHDAY=0';\n\n// after\n$rrule = 'FREQ=MONTHLY;BYMONTHDAY=-1';","handlingStrategy":"validation","validationCode":"foreach (array('BYMONTHDAY', 'BYYEARDAY') as $key) {\n  if (isset($parts[$key])) {\n    foreach (explode(',', $parts[$key]) as $value) {\n      if ((int)$value === 0) {\n        throw new Exception($key.' must not contain zero.');\n      }\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  $rule = PhutilCalendarRecurrenceRule::newFromRRule($rrule);\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'must not be zero') !== false) {\n    // Strip zero entries and retry.\n  }\n}","preventionTips":["Filter zeros out of day lists at the input boundary","Use -1 for 'last day of month', never 0","When converting UI offsets to month days, unit-test the boundary at index 0"],"tags":["rrule","icalendar","rfc5545","calendar","zero-value"],"backgroundTag":"rrule-validation-failed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}