{"record":{"id":"301495cd8492498f","repo":"phacility/phabricator","slug":"rrule-evaluation-failed-to-generate-more-events-in","errorCode":null,"errorMessage":"RRULE evaluation failed to generate more events in the next 100 years. This RRULE is likely invalid or degenerate.","messagePattern":"RRULE evaluation failed to generate more events in the next 100 years\\. This RRULE is likely invalid or degenerate\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php","lineNumber":1071,"sourceCode":"    $this->stateWeek = array_pop($this->setWeeks);\n  }\n\n  protected function nextYear() {\n    $this->stateYear = $this->cursorYear;\n\n    $frequency = $this->getFrequency();\n    $is_yearly = ($frequency === self::FREQUENCY_YEARLY);\n\n    if ($is_yearly) {\n      $interval = $this->getInterval();\n    } else {\n      $interval = 1;\n    }\n\n    $this->cursorYear = $this->cursorYear + $interval;\n\n    if ($this->cursorYear > ($this->baseYear + 100)) {\n      throw new Exception(\n        pht(\n          'RRULE evaluation failed to generate more events in the next 100 '.\n          'years. This RRULE is likely invalid or degenerate.'));\n    }\n\n  }\n\n  private function newSecondsSet($interval, $set) {\n    // TODO: This doesn't account for leap seconds. In theory, it probably\n    // should, although this shouldn't impact any real events.\n    $seconds_in_minute = 60;\n\n    if ($this->cursorSecond >= $seconds_in_minute) {\n      $this->cursorSecond -= $seconds_in_minute;\n      return array();\n    }\n\n    list($cursor, $result) = $this->newIteratorSet(","sourceCodeStart":1053,"sourceCodeEnd":1089,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php#L1053-L1089","documentation":"This is the engine's infinite-loop guard: while advancing the year cursor to generate occurrences, more than 100 years elapsed past the rule's base year without a single event being produced. It means the rule's constraints can never be satisfied (a degenerate rule), for example BYMONTH=2 combined with BYMONTHDAY=30 since February 30th never exists.","triggerScenarios":"RRULEs with impossible constraint intersections, e.g. 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=30', 'FREQ=YEARLY;BYYEARDAY=366' evaluated only across non-leap years, or rules whose COUNT/UNTIL/WKST interplay filters out every candidate occurrence.","commonSituations":"User-authored recurring events with contradictory filters; imports from tools that emit unsatisfiable rules; rules that only match extremely rare dates (Feb 29 with strict yearly frequency) hitting the 100-year horizon before matching.","solutions":["Fix the impossible constraint (e.g. change BYMONTHDAY=30 in February to a day that exists)","If the rule legitimately matches rarely, verify it with a small result limit and a bounded date range before trusting it","Wrap evaluation in a try/catch that surfaces a friendly 'this recurrence never produces events' message to the user instead of a raw exception"],"exampleFix":"// before\n$rrule = 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=30';\n\n// after\n$rrule = 'FREQ=YEARLY;BYMONTH=2;BYMONTHDAY=28';","handlingStrategy":"try-catch","validationCode":"// No static check can prove satisfiability cheaply; do a bounded probe:\ntry {\n  $probe = PhutilCalendarRecurrenceRule::newFromRRule($rrule);\n  $events = id(new PhutilCalendarRecurrenceSet())\n    ->setSource($probe)\n    ->getEventsBetween($start, $start + phutil_units('2 years in seconds'), 1);\n  if (!$events) {\n    throw new Exception('Rule produced no events in a 2-year probe window.');\n  }\n} catch (Exception $ex) {\n  throw new Exception(pht('Degenerate recurrence rule: %s', $rrule));\n}","typeGuard":null,"tryCatchPattern":"try {\n  $events = $rule->getEvents();\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'next 100 years') !== false) {\n    // Rule never matches: reject the user's input with a friendly message.\n    throw new Exception(pht('This recurrence never produces any events.'));\n  }\n  throw $ex;\n}","preventionTips":["Always evaluate imported rules with a small result limit inside a try/catch before accepting them","Warn users when constraint combinations are suspicious (day >= 29 in short months, BYYEARDAY=366 without leap years)","Treat the 100-year guard as a backstop, not validation — catch it and report upstream"],"tags":["rrule","degenerate-rule","icalendar","calendar","infinite-loop-guard"],"backgroundTag":"rrule-validation-failed","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}