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

  1. Keep exactly one status per special role: one default, one closed-default, one duplicate.
  2. When introducing a replacement status, strip the 'special' key from the old status in the same config edit.
  3. 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

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


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