phacility/phabricator · error · Exception

Two different task priorities ("%s" and "%s") have the same

Error message

Two different task priorities ("%s" and "%s") have the same keyword ("%s"). Keywords must uniquely identify priorities.

What it means

Priority keywords must uniquely identify a priority across the whole maniphest.priorities map. The validator accumulates every keyword in $all_keywords keyed by keyword -> priority name; when a later level reuses a keyword already claimed by a different priority, it throws with (new name, previous name, keyword). This matters because mail/Places commands like '!priority high' resolve by keyword alone.

Source

Thrown at src/applications/maniphest/constants/ManiphestTaskPriority.php:247

          'disabled' => 'optional bool',
        ));

      $keywords = $value['keywords'];
      foreach ($keywords as $keyword) {
        if (!self::isValidPriorityKeyword($keyword)) {
          throw new Exception(
            pht(
              'Key "%s" is not a valid priority keyword. Priority keywords '.
              'must be 1-64 alphanumeric characters and cannot be '.
              'exclusively digits. For example, "%s" or "%s" are '.
              'reasonable choices.',
              $keyword,
              'low',
              'critical'));
        }

        if (isset($all_keywords[$keyword])) {
          throw new Exception(
            pht(
              'Two different task priorities ("%s" and "%s") have the same '.
              'keyword ("%s"). Keywords must uniquely identify priorities.',
              $value['name'],
              $all_keywords[$keyword],
              $keyword));
        }

        $all_keywords[$keyword] = $value['name'];
      }
    }
  }

}

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Give each level disjoint keywords, e.g. 'high' and 'higher'/'unbreak-now', never the same token twice.
  2. After editing, scan the config for duplicate keywords programmatically before saving.
  3. If a keyword genuinely must move to another level, delete it from the old level's list in the same edit.

Example fix

// before
'90' => array('name' => 'High',      'keywords' => array('high')),
'80' => array('name' => 'Very High', 'keywords' => array('high', 'urgent')),

// after
'90' => array('name' => 'High',      'keywords' => array('high')),
'80' => array('name' => 'Very High', 'keywords' => array('very-high', 'urgent')))
Defensive patterns

Strategy: validation

Validate before calling

$seen = array();
foreach ($priorities as $const => $spec) {
  foreach ($spec['keywords'] as $kw) {
    if (isset($seen[$kw])) {
      throw new InvalidArgumentException("Keyword '$kw' used by both {$seen[$kw]} and {$spec['name']}");
    }
    $seen[$kw] = $spec['name'];
  }
}

Prevention

When it happens

Trigger: Two levels both listing 'high': {"90": {"name": "High", "keywords": ["high"]}, "80": {"name": "Very High", "keywords": ["high", "urgent"]}}. The second occurrence triggers the exception.

Common situations: Merging two priority configs (e.g. after a team merger) where both defined 'high'. Copy-pasting a level and editing the name but forgetting the keywords list. Renaming a priority but keeping the old keyword on the new one.

Related errors


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