phacility/phabricator · error · Exception

RRULE dictionary includes unknown key "%s". Expected keys ar

Error message

RRULE dictionary includes unknown key "%s". Expected keys are: %s.

What it means

PhutilCalendarRecurrenceRule::newFromDictionary() validates every key of the supplied dictionary against the known RRULE properties (FREQ, INTERVAL, BYSECOND, BYMINUTE, BYHOUR, BYDAY, BYMONTH, BYMONTHDAY, BYEARDAY, BYWEEKNO, BYSETPOS, WKST, UNTIL, COUNT). Any unrecognized key aborts with the expected-key list.

Source

Thrown at src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php:186

          'INTERVAL',
          'BYSECOND',
          'BYMINUTE',
          'BYHOUR',
          'BYDAY',
          'BYMONTH',
          'BYMONTHDAY',
          'BYYEARDAY',
          'BYWEEKNO',
          'BYSETPOS',
          'WKST',
          'UNTIL',
          'COUNT',
        ));
    }

    foreach ($dict as $key => $value) {
      if (empty($expect[$key])) {
        throw new Exception(
          pht(
            'RRULE dictionary includes unknown key "%s". Expected keys '.
            'are: %s.',
            $key,
            implode(', ', array_keys($expect))));
      }
    }

    $rrule = id(new self())
      ->setFrequency(idx($dict, 'FREQ'))
      ->setInterval(idx($dict, 'INTERVAL', 1))
      ->setBySecond(idx($dict, 'BYSECOND', array()))
      ->setByMinute(idx($dict, 'BYMINUTE', array()))
      ->setByHour(idx($dict, 'BYHOUR', array()))
      ->setByDay(idx($dict, 'BYDAY', array()))
      ->setByMonth(idx($dict, 'BYMONTH', array()))
      ->setByMonthDay(idx($dict, 'BYMONTHDAY', array()))
      ->setByYearDay(idx($dict, 'BYYEARDAY', array()))

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Filter the dict to known keys first: array_intersect_key($dict, array_flip($knownKeys)).
  2. Uppercase keys: array_change_key_case($dict, CASE_UPPER).
  3. Strip unsupported extension properties (BYEASTER, X-*) before constructing the rule.

Example fix

// before
$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict); // $dict has 'BYEASTER'
// after
unset($dict['BYEASTER']);
$dict = array_change_key_case($dict, CASE_UPPER);
$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);
Defensive patterns

Strategy: type-guard

Validate before calling

$known = array('FREQ','INTERVAL','BYSECOND','BYMINUTE','BYHOUR','BYDAY',
  'BYMONTH','BYMONTHDAY','BYYEARDAY','BYWEEKNO','BYSETPOS','WKST','UNTIL','COUNT');
$dict = array_change_key_case((array)$dict, CASE_UPPER);
$dict = array_intersect_key($dict, array_flip($known)); // drop unknown keys
$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);

Type guard

function rruleDictHasKnownKeys(array $dict) {
  $known = array_flip(array('FREQ','INTERVAL','BYSECOND','BYMINUTE','BYHOUR',
    'BYDAY','BYMONTH','BYMONTHDAY','BYYEARDAY','BYWEEKNO','BYSETPOS','WKST',
    'UNTIL','COUNT'));
  return count(array_diff_key(array_change_key_case($dict, CASE_UPPER), $known)) === 0;
}

Try / catch

try {
  $rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);
} catch (Exception $ex) {
  // log the offending key from the message and drop it, then retry once
  phlog($ex->getMessage());
  unset($dict[$badKey]);
}

Prevention

When it happens

Trigger: A dict containing 'BYEASTER' (common non-standard extension), lowercase keys like 'freq', a typo such as 'COUNTS', or an ICS parser leaking 'X-...' extension properties into the RRULE dict.

Common situations: Feeding raw parsed ICS RRULE lines into the constructor; vendors adding private extensions; case mismatches after lowercasing keys for storage.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/020480463cae4a21. Report an issue: GitHub.