phacility/phabricator · error · Exception
RRULE specifies BYSETPOS with FREQ "%s", but this is invalid
Error message
RRULE specifies BYSETPOS with FREQ "%s", but this is invalid.
What it means
Evaluating BYSETPOS requires the rule engine to rewind its cursor one interval, and PhutilCalendarRecurrenceRule only implements rewinding down to MINUTELY. The default branch of the rewind switch therefore throws for FREQ=SECONDLY (or any unrecognized frequency) rather than silently mis-evaluating the set positions.
Source
Thrown at src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php:671
case self::FREQUENCY_WEEKLY:
$this->cursorWeek -= $interval;
$this->rewindWeek();
break;
case self::FREQUENCY_DAILY:
$this->cursorDay -= $interval;
$this->rewindDay();
break;
case self::FREQUENCY_HOURLY:
$this->cursorHour -= $interval;
$this->rewindHour();
break;
case self::FREQUENCY_MINUTELY:
$this->cursorMinute -= $interval;
$this->rewindMinute();
break;
case self::FREQUENCY_SECONDLY:
default:
throw new Exception(
pht(
'RRULE specifies BYSETPOS with FREQ "%s", but this is invalid.',
$frequency));
}
}
// We can generate events from before the cursor when evaluating rules
// with BYSETPOS or FREQ=WEEKLY.
$this->minimumEpoch = $this->getStartDateTime()->getEpoch();
$cursor_state = array(
'year' => $this->cursorYear,
'month' => $this->cursorMonth,
'week' => $this->cursorWeek,
'day' => $this->cursorDay,
'hour' => $this->cursorHour,
);
View on GitHub (pinned to 5720a38cfe)
Solutions
- Use a coarser frequency (MINUTELY or larger) when BYSETPOS is required
- Drop the BYSETPOS part if second-level recurrence is the real requirement
- Reject FREQ=SECONDLY + BYSETPOS combinations during RRULE pre-validation before import
Example fix
// before $rrule = 'FREQ=SECONDLY;BYSECOND=0,30;BYSETPOS=1'; // after $rrule = 'FREQ=MINUTELY;BYSECOND=0,30;BYSETPOS=1';
Defensive patterns
Strategy: validation
Validate before calling
if (isset($parts['BYSETPOS']) && idx($parts, 'FREQ') === 'SECONDLY') {
throw new Exception('BYSETPOS cannot be combined with FREQ=SECONDLY.');
} Try / catch
try {
$events = $rule->getEvents();
} catch (Exception $ex) {
if (strpos($ex->getMessage(), 'BYSETPOS') !== false) {
$events = $rule->setBySetPosition(array())->getEvents();
}
} Prevention
- Bound supported frequencies in your product surface (most UIs never need SECONDLY)
- Reject sub-minute frequencies at the input layer unless explicitly supported
- Fuzz-test generated RRULEs across all FREQ values during development
When it happens
Trigger: An RRULE that combines BYSETPOS with a sub-minute frequency, e.g. 'FREQ=SECONDLY;BYSETPOS=1', or code that sets frequency to FREQUENCY_SECONDLY together with setBySetPosition(...) and then triggers evaluation/rewind.
Common situations: Rules synthesized by test harnesses or tooling that sweep all frequencies; malformed imported RRULEs where FREQ was truncated (e.g. 'FREQ=SECOND;...' parsed into an unexpected value).
Related errors
- RRULE BYDAY value "%s" has an offset with magnitude "%s", bu
- RRULE specifies BYMONTHDAY with FREQ set to WEEKLY, which vi
- RRULE specifies BYYEARDAY with FREQ of DAILY, WEEKLY or MONT
- RRULE evaluation failed to generate more events in the next
- Value "%s" in RRULE "%s" parameter is invalid: it must be be
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/3c94364fd38999f7.
Report an issue: GitHub.