{"record":{"id":"020480463cae4a21","repo":"phacility/phabricator","slug":"rrule-dictionary-includes-unknown-key-s-expect","errorCode":null,"errorMessage":"RRULE dictionary includes unknown key \"%s\". Expected keys are: %s.","messagePattern":"RRULE dictionary includes unknown key \"(.+?)\"\\. Expected keys are: (.+?)\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php","lineNumber":186,"sourceCode":"          'INTERVAL',\n          'BYSECOND',\n          'BYMINUTE',\n          'BYHOUR',\n          'BYDAY',\n          'BYMONTH',\n          'BYMONTHDAY',\n          'BYYEARDAY',\n          'BYWEEKNO',\n          'BYSETPOS',\n          'WKST',\n          'UNTIL',\n          'COUNT',\n        ));\n    }\n\n    foreach ($dict as $key => $value) {\n      if (empty($expect[$key])) {\n        throw new Exception(\n          pht(\n            'RRULE dictionary includes unknown key \"%s\". Expected keys '.\n            'are: %s.',\n            $key,\n            implode(', ', array_keys($expect))));\n      }\n    }\n\n    $rrule = id(new self())\n      ->setFrequency(idx($dict, 'FREQ'))\n      ->setInterval(idx($dict, 'INTERVAL', 1))\n      ->setBySecond(idx($dict, 'BYSECOND', array()))\n      ->setByMinute(idx($dict, 'BYMINUTE', array()))\n      ->setByHour(idx($dict, 'BYHOUR', array()))\n      ->setByDay(idx($dict, 'BYDAY', array()))\n      ->setByMonth(idx($dict, 'BYMONTH', array()))\n      ->setByMonthDay(idx($dict, 'BYMONTHDAY', array()))\n      ->setByYearDay(idx($dict, 'BYYEARDAY', array()))","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/calendar/parser/data/PhutilCalendarRecurrenceRule.php#L168-L204","documentation":"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.","triggerScenarios":"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.","commonSituations":"Feeding raw parsed ICS RRULE lines into the constructor; vendors adding private extensions; case mismatches after lowercasing keys for storage.","solutions":["Filter the dict to known keys first: array_intersect_key($dict, array_flip($knownKeys)).","Uppercase keys: array_change_key_case($dict, CASE_UPPER).","Strip unsupported extension properties (BYEASTER, X-*) before constructing the rule."],"exampleFix":"// before\n$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict); // $dict has 'BYEASTER'\n// after\nunset($dict['BYEASTER']);\n$dict = array_change_key_case($dict, CASE_UPPER);\n$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);","handlingStrategy":"type-guard","validationCode":"$known = array('FREQ','INTERVAL','BYSECOND','BYMINUTE','BYHOUR','BYDAY',\n  'BYMONTH','BYMONTHDAY','BYYEARDAY','BYWEEKNO','BYSETPOS','WKST','UNTIL','COUNT');\n$dict = array_change_key_case((array)$dict, CASE_UPPER);\n$dict = array_intersect_key($dict, array_flip($known)); // drop unknown keys\n$rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);","typeGuard":"function rruleDictHasKnownKeys(array $dict) {\n  $known = array_flip(array('FREQ','INTERVAL','BYSECOND','BYMINUTE','BYHOUR',\n    'BYDAY','BYMONTH','BYMONTHDAY','BYYEARDAY','BYWEEKNO','BYSETPOS','WKST',\n    'UNTIL','COUNT'));\n  return count(array_diff_key(array_change_key_case($dict, CASE_UPPER), $known)) === 0;\n}","tryCatchPattern":"try {\n  $rule = PhutilCalendarRecurrenceRule::newFromDictionary($dict);\n} catch (Exception $ex) {\n  // log the offending key from the message and drop it, then retry once\n  phlog($ex->getMessage());\n  unset($dict[$badKey]);\n}","preventionTips":["Uppercase and filter dict keys against the known RRULE set before constructing rules.","Strip nonstandard extension properties (BYEASTER, X-*) at ICS ingest time.","Do not round-trip RRULE dicts through storage that lowercases keys."],"tags":["phutil","calendar","rrule","validation","unknown-key"],"backgroundTag":"unknown-dictionary-key","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}