{"record":{"id":"4baa2d3a93eb0273","repo":"phacility/phabricator","slug":"value-for-key-s-should-be-a-dictionary-4baa2d","errorCode":null,"errorMessage":"Value for key \"%s\" should be a dictionary.","messagePattern":"Value for key \"(.+?)\" should be a dictionary\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/maniphest/constants/ManiphestTaskStatus.php","lineNumber":274,"sourceCode":"  }\n\n  /**\n   * @task validate\n   */\n  public static function validateConfiguration(array $config) {\n    foreach ($config as $key => $value) {\n      if (!self::isValidStatusConstant($key)) {\n        throw new Exception(\n          pht(\n            'Key \"%s\" is not a valid status constant. Status constants '.\n            'must be 1-64 alphanumeric characters and cannot be exclusively '.\n            'digits. For example, \"%s\" or \"%s\" are reasonable choices.',\n            $key,\n            'open',\n            'closed'));\n      }\n      if (!is_array($value)) {\n        throw new Exception(\n          pht(\n            'Value for key \"%s\" should be a dictionary.',\n            $key));\n      }\n\n      PhutilTypeSpec::checkMap(\n        $value,\n        array(\n          'name' => 'string',\n          'name.full' => 'optional string',\n          'name.action' => 'optional string',\n          'closed' => 'optional bool',\n          'special' => 'optional string',\n          'transaction.icon' => 'optional string',\n          'transaction.color' => 'optional string',\n          'silly' => 'optional bool',\n          'prefixes' => 'optional list<string>',\n          'suffixes' => 'optional list<string>',","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/maniphest/constants/ManiphestTaskStatus.php#L256-L292","documentation":"Each value in the maniphest.statuses dict must be an array/dict. After the status constant passes, !is_array($value) throws naming the key. The subsequent PhutilTypeSpec::checkMap accepts 'name' (string) with optional 'name.full', 'name.action', 'closed' (bool), 'locked', 'special', and task/transaction type fields.","triggerScenarios":"A statuses entry whose value is a scalar or null: {\"open\": \"Open\"} or {\"resolved\": null}. The per-entry type gate fails before field-level checks like 'locked' or 'special' run.","commonSituations":"Hand-editing JSON where braces around one entry got dropped, producing a bare string. Template-based config generation that leaves optional entries as null instead of omitting them. YAML merges that collapsed a mapping into a scalar.","solutions":["Give every status constant a full dict value: {\"open\": {\"name\": \"Open\"}}.","Omit statuses you do not want rather than setting them to null.","Validate with is_array($statuses[$key]) for all keys before bin/config set maniphest.statuses."],"exampleFix":"// before\narray('open' => 'Open')\n\n// after\narray('open' => array('name' => 'Open'))","handlingStrategy":"validation","validationCode":"foreach ($statuses as $constant => $spec) {\n  if (!is_array($spec)) {\n    throw new InvalidArgumentException(\"Status '$constant' value must be a dict\");\n  }\n}","typeGuard":"function is_status_spec($v) { return is_array($v) && isset($v['name']) && is_string($v['name']); }","tryCatchPattern":null,"preventionTips":["Every constant maps to a dict containing at least 'name'.","Delete unused statuses instead of nulling them.","Lint JSON structure ( braces per entry) before bin/config set."],"tags":["phabricator","maniphest","configuration","status","type-validation"],"backgroundTag":"config-schema-validation","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}