phacility/phabricator · error · Exception

Project icons must have unique keys, but two icons share the

Error message

Project icons must have unique keys, but two icons share the same key ("%s").

What it means

Icon keys must be unique across the whole projects.icons list. While collecting keys into a lookup array, a second specification reusing an existing key throws and names the duplicated key, before default/milestone resolution continues.

Source

Thrown at src/applications/project/icon/PhabricatorProjectIconSet.php:255

      if ($special !== null) {
        if (empty($valid[$special])) {
          throw new Exception(
            pht(
              'Icon special attribute "%s" is not valid. Recognized special '.
              'attributes are: %s.',
              $special,
              implode(', ', array_keys($valid))));
        }
      }
    }

    $default = null;
    $milestone = null;
    $keys = array();
    foreach ($config as $idx => $value) {
      $key = $value['key'];
      if (isset($keys[$key])) {
        throw new Exception(
          pht(
            'Project icons must have unique keys, but two icons share the '.
            'same key ("%s").',
            $key));
      } else {
        $keys[$key] = true;
      }

      $is_disabled = idx($value, 'disabled');

      $image = idx($value, 'image');
      if ($image !== null) {
        $builtin = idx($value, 'image');
        $builtin_map = id(new PhabricatorFilesOnDiskBuiltinFile())
          ->getProjectBuiltinFiles();
        $builtin_map = array_flip($builtin_map);

        $root = dirname(phutil_get_library_root('phabricator'));

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Rename one of the duplicates to an unused lowercase key (letters only).
  2. If you meant to replace a builtin icon, remove the original entry so only one spec carries that key.

Example fix

// before: two entries share key "tag"
[{"key":"tag","name":"Tag","icon":"fa-tags"}, {"key":"tag","name":"Tag2","icon":"fa-tag"}]
// after
[{"key":"tag","name":"Tag","icon":"fa-tags"}, {"key":"label","name":"Label","icon":"fa-tag"}]
Defensive patterns

Strategy: validation

Validate before calling

$keys = array();
foreach ($config as $entry) {
  if (isset($keys[$entry['key']])) {
    throw new Exception('duplicate icon key: '.$entry['key']);
  }
  $keys[$entry['key']] = true;
}

Type guard

function has_unique_icon_keys(array $config) {
  $keys = array_column($config, 'key');
  return count($keys) === count(array_unique($keys));
}

Prevention

When it happens

Trigger: Appending a custom icon with "key": "tag" while the builtin tag icon is still in the list, or merging two config fragments that both define the same key.

Common situations: Extending the default list instead of replacing it, and merging icon configs from multiple sources.

Related errors


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