phacility/phabricator · error · Exception
Only patches in the '%s' namespace may contain '%s' keys.
Error message
Only patches in the '%s' namespace may contain '%s' keys.
What it means
The 'legacy' key marks patches that predate the modern patch system and is meaningful only for Phabricator's own first-party storage. Third-party applications and libraries (any namespace other than 'phabricator') cannot declare legacy patches, so the loader rejects the key outright for them.
Source
Thrown at src/infrastructure/storage/patch/PhabricatorSQLPatchList.php:140
$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',
'legacy'));
}
} else {
$patch['legacy'] = false;
}
if (!array_key_exists('phase', $patch)) {
$patch['phase'] = $default_phase;
}
$patch_phase = $patch['phase'];
if (!isset($phases[$patch_phase])) {
throw new Exception(
pht(View on GitHub (pinned to 5720a38cfe)
Solutions
- Delete the 'legacy' key from the patch in your application's list — it is meaningless outside the 'phabricator' namespace.
- If you genuinely need first-party legacy semantics, the code must live in the phabricator namespace, which only the upstream project uses.
- Re-run 'bin/storage status' to confirm the list loads.
Example fix
// before (namespace: 'myapp')
'0000.initial.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('0000.initial.sql'),
'legacy' => true,
),
// after
'0000.initial.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('0000.initial.sql'),
); Defensive patterns
Strategy: validation
Validate before calling
// Only the phabricator namespace may mark patches legacy:
$list = new MyApplicationPatchList();
if ($list->getNamespace() !== 'phabricator') {
foreach ($list->getPatches() as $patch) {
if (array_key_exists('legacy', $patch)) {
throw new Exception('Only the phabricator namespace may use the legacy key');
}
}
} Prevention
- Do not copy the head of Phabricator's core patch list (legacy-era entries) into application lists.
- Treat 'legacy' as a frozen upstream-only flag; new patches never need it.
When it happens
Trigger: A patch list whose getNamespace() returns something other than 'phabricator' returns a patch containing a 'legacy' key. This usually happens when copying an early patch from phabricator's own list into an application's list.
Common situations: Bootstrapping a new application by copying the head of Phabricator's core patch list (which starts with legacy patches); porting old pre-modern-patch-system definitions into a fork's extension.
Related errors
- %s '%s' has a patch, '%s', with an unknown property, '%s'.Pa
- %s '%s' has a patch with a numeric key, '%s'. Patches must u
- %s '%s' has a patch with a colon in the key name, '%s'. Patc
- %s '%s' has a patch '%s' which duplicates an existing patch
- Storage patch "%s" specifies it should apply in phase "%s",
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/c3f2aac27c96c355.
Report an issue: GitHub.