phacility/phabricator · error · Exception
Configuration has two statuses both marked with the special
Error message
Configuration has two statuses both marked with the special attribute "%s" ("%s" and "%s"). There should be only one. What it means
The 'special' attribute marks a status as filling a unique system role: SPECIAL_DEFAULT (default for new tasks), SPECIAL_CLOSED (default when closing), SPECIAL_DUPLICATE (closing as duplicate). The validator tracks each special value in $special_map; if a second status claims a special role already taken, it throws with (special role, previous status constant, new status constant).
Source
Thrown at src/applications/maniphest/constants/ManiphestTaskStatus.php:333
throw new Exception(
pht(
'Task status ("%s") has unrecognized value for "locked" '.
'configuration ("%s"). Supported values are: "%s", "%s".',
$key,
$locked,
self::LOCKED_COMMENTS,
self::LOCKED_EDITS));
}
$special_map = array();
foreach ($config as $key => $value) {
$special = idx($value, 'special');
if (!$special) {
continue;
}
if (isset($special_map[$special])) {
throw new Exception(
pht(
'Configuration has two statuses both marked with the special '.
'attribute "%s" ("%s" and "%s"). There should be only one.',
$special,
$special_map[$special],
$key));
}
switch ($special) {
case self::SPECIAL_DEFAULT:
if (!empty($value['closed'])) {
throw new Exception(
pht(
'Status "%s" is marked as default, but it is a closed '.
'status. The default status should be an open status.',
$key));
}
break;View on GitHub (pinned to 5720a38cfe)
Solutions
- Keep exactly one status per special role: one default, one closed-default, one duplicate.
- When introducing a replacement status, strip the 'special' key from the old status in the same config edit.
- After editing, grep your config for each special value and confirm it appears exactly once.
Example fix
// before
'open' => array('name' => 'Open', 'special' => 'default'),
'triage' => array('name' => 'Triage', 'special' => 'default'),
// after
'open' => array('name' => 'Open'),
'triage' => array('name' => 'Triage', 'special' => 'default'), Defensive patterns
Strategy: validation
Validate before calling
$roles = array();
foreach ($statuses as $constant => $spec) {
if (!empty($spec['special'])) {
if (isset($roles[$spec['special']])) {
throw new InvalidArgumentException("Special '{$spec['special']}' claimed by both {$roles[$spec['special']]} and $constant");
}
$roles[$spec['special']] = $constant;
}
} Prevention
- Each special role (default/closed/duplicate) appears exactly once.
- Strip the special marker from a status before assigning it elsewhere.
- Grep the config for 'special' after every status edit.
When it happens
Trigger: Two entries with "special": "default" in maniphest.statuses, e.g. both 'open' and 'triage' marked default. Likewise duplicated 'closed' or 'duplicate' special values.
Common situations: Editing the default statuses to add a new 'triage' intake status and marking it default without removing "special": "default" from 'open'. Copy-pasting a closed status (e.g. 'resolved') to make 'wontfix' and leaving the "special": "closed" field in both.
Related errors
- Status "%s" is marked as default, but it is a closed status.
- Status "%s" is marked as the default status for closing task
- Status "%s" is marked as the status for closing tasks as dup
- Key "%s" is not a valid status constant. Status constants mu
- Value for key "%s" should be a dictionary.
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/a2fae86ddcae8e82.
Report an issue: GitHub.