phacility/phabricator · error · Exception
%s '%s' has a patch '%s' which duplicates an existing patch
Error message
%s '%s' has a patch '%s' which duplicates an existing patch key.
What it means
Full patch keys ('namespace:key') must be globally unique across every installed patch list. When two patches resolve to the same full key, Phabricator cannot decide which one defines the schema state, so buildAllPatches() aborts. Note the duplicate is detected by full key, so two lists sharing one namespace, or one list repeating a key, both trip this.
Source
Thrown at src/infrastructure/storage/patch/PhabricatorSQLPatchList.php:125
get_class($patch_list),
$key));
}
if (strpos($key, ':') !== false) {
throw new Exception(
pht(
"%s '%s' has a patch with a colon in the key name, '%s'. ".
"Patch keys may not contain colons.",
__CLASS__,
get_class($patch_list),
$key));
}
$namespace = $patch_list->getNamespace();
$full_key = "{$namespace}:{$key}";
if (isset($specs[$full_key])) {
throw new Exception(
pht(
"%s '%s' has a patch '%s' which duplicates an ".
"existing patch key.",
__CLASS__,
get_class($patch_list),
$key));
}
$patch['key'] = $key;
$patch['fullKey'] = $full_key;
$patch['dead'] = (bool)idx($patch, 'dead', false);
if (isset($patch['legacy'])) {
if ($namespace != 'phabricator') {
throw new Exception(
pht(
"Only patches in the '%s' namespace may contain '%s' keys.",
'phabricator',View on GitHub (pinned to 5720a38cfe)
Solutions
- Search the named patch list class for the reported key; if it appears twice, rename the newer entry.
- If you intended to copy an existing patch, give the new entry its own unique date-prefixed key.
- If two classes share a namespace, fix getNamespace() so each application/library has a distinct one.
- Re-run 'bin/storage status' to confirm uniqueness.
Example fix
// before
return array(
'20180102.gadget.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20180102.gadget.sql'),
),
'20180102.gadget.sql' => array( // duplicate key
'type' => 'sql',
'name' => $this->getPatchPath('20180103.gadget.index.sql'),
),
);
// after
return array(
'20180102.gadget.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20180102.gadget.sql'),
),
'20180103.gadget.index.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20180103.gadget.index.sql'),
),
); Defensive patterns
Strategy: validation
Validate before calling
// Detect duplicate full keys across every installed patch list before storage runs:
PhabricatorSQLPatchList::buildAllPatches(); // throws on duplicates
// Or, for a single list:
$keys = array_keys((new MyApplicationPatchList())->getPatches());
if (count($keys) !== count(array_unique($keys))) {
throw new Exception('Duplicate patch keys in list');
} Prevention
- After duplicating a patch entry to start a new one, rename the key in the same edit — review the diff for repeated keys.
- Give each application/library a unique getNamespace() so full keys cannot collide across lists.
When it happens
Trigger: The same patch key appears twice in one getPatches() list (classic copy-paste: duplicating the previous entry and forgetting to change its key), or two PhabricatorSQLPatchList subclasses declare the same getNamespace() with overlapping keys.
Common situations: Copy-pasting the most recent patch entry to start a new one and forgetting to rename the key; merging two branches that both added a patch with the same date-prefixed name; accidentally registering two patch list classes with the same namespace.
Related errors
- This contact number is already in use.
- This public key is already associated with another user or d
- Subtype configuration is invalid: two subtypes use the same
- Both '%s' and '%s' define a custom field with field key '%s'
- %s '%s' has a patch, '%s', with an unknown property, '%s'.Pa
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/f69641e6c5b91130.
Report an issue: GitHub.