phacility/phabricator · error · Exception
Unexpected value "%s" in "%s" RULE property: expected an int
Error message
Unexpected value "%s" in "%s" RULE property: expected an integer.
What it means
While normalizing an RRULE dictionary, the scalar integer properties COUNT and INTERVAL must match /^\d+$/ (a non-negative integer string). Negative numbers, decimals, or non-numeric values throw before the cast to int. Note the message text says 'RULE property' (the RRULE spelling used elsewhere is inconsistent).
Source
Thrown at src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php:280
'BYHOUR',
'BYMONTH',
'BYMONTHDAY',
'BYYEARDAY',
'BYWEEKNO',
'BYSETPOS',
));
$int_values = array_fuse(
array(
'COUNT',
'INTERVAL',
));
foreach ($dict as $key => $value) {
if (isset($int_values[$key])) {
// None of these values may be negative.
if (!preg_match('/^\d+\z/', $value)) {
throw new Exception(
pht(
'Unexpected value "%s" in "%s" RULE property: expected an '.
'integer.',
$value,
$key));
}
$dict[$key] = (int)$value;
}
if (isset($int_lists[$key])) {
foreach ($value as $k => $v) {
if (!preg_match('/^-?\d+\z/', $v)) {
throw new Exception(
pht(
'Unexpected value "%s" in "%s" RRULE property: expected '.
'only integers.',
$v,
$key));View on GitHub (pinned to 5720a38cfe)
Solutions
- Coerce values before building the dict: $dict['COUNT'] = (string)(int)$count with $count >= 0.
- Reject or sanitize external ICS values that do not match ^\d+$.
- Remember COUNT=0 passes this check but later throws in setCount(); keep COUNT >= 1.
Example fix
// before $dict['INTERVAL'] = '2x'; // after $dict['INTERVAL'] = (string)(int)$interval;
Defensive patterns
Strategy: type-guard
Validate before calling
// Coerce scalar integer fields before building the dict:
foreach (array('COUNT', 'INTERVAL') as $k) {
if (isset($dict[$k]) && preg_match('/^\d+$/', (string)$dict[$k])) {
$dict[$k] = (string)(int)$dict[$k];
} else {
unset($dict[$k]); // drop invalid values instead of throwing later
}
} Type guard
function isNonNegativeIntegerString($value) {
return is_string($value) && preg_match('/^\d+$/', $value)
|| is_int($value) && $value >= 0;
} Try / catch
try {
$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);
} catch (Exception $ex) {
phlog($ex->getMessage()); // identifies the offending property and value
continue;
} Prevention
- Cast COUNT/INTERVAL to ints at the boundary where external data enters.
- Reject '+'-prefixed, decimal, or negative values early.
- Remember COUNT must be >= 1 to survive setCount() as well.
When it happens
Trigger: Dict with 'COUNT' => '-5', 'INTERVAL' => '1.5', 'COUNT' => 'weekly', or values that arrived as free text from an ICS producer. Plain PHP ints are fine because preg_match stringifies them.
Common situations: Hand-built dicts from user input; ICS files with malformed RRULE parts; JSON round-trips that mangle numbers into strings like '1.0'.
Related errors
- RRULE dictionary includes unknown key "%s". Expected keys ar
- Unexpected value "%s" in "%s" RRULE property: expected only
- RRULE INTERVAL "%s" is invalid: interval must be an integer.
- Weekday "%s" is not a valid weekday constant. Valid constant
- RRULE COUNT value "%s" is invalid: count must be at least 1.
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/b1e3ca80a77db30a.
Report an issue: GitHub.