phacility/phabricator · error · Exception
Patch '%s' is missing key 'after', and is the first patch in
Error message
Patch '%s' is missing key 'after', and is the first patch in the patch list '%s', so its application order can not be determined implicitly. The first patch in a patch list must list the patch or patches it depends on explicitly.
What it means
For convenience, a patch without an 'after' key is implicitly ordered after the previous patch in the same namespace and phase. That inference is impossible for the very first patch in the default phase of a patch list (there is no previous patch), so the loader requires its dependency list to be explicit to keep upgrade order deterministic.
Source
Thrown at src/infrastructure/storage/patch/PhabricatorSQLPatchList.php:170
}
$patch_phase = $patch['phase'];
if (!isset($phases[$patch_phase])) {
throw new Exception(
pht(
'Storage patch "%s" specifies it should apply in phase "%s", '.
'but this phase is unrecognized. Valid phases are: %s.',
$full_key,
$patch_phase,
implode(', ', array_keys($phases))));
}
$last_key = $last_keys[$patch_phase];
if (!array_key_exists('after', $patch)) {
if ($last_key === null && $patch_phase === $default_phase) {
throw new Exception(
pht(
"Patch '%s' is missing key 'after', and is the first patch ".
"in the patch list '%s', so its application order can not be ".
"determined implicitly. The first patch in a patch list must ".
"list the patch or patches it depends on explicitly.",
$full_key,
get_class($patch_list)));
} else {
if ($last_key === null) {
$patch['after'] = array();
} else {
$patch['after'] = array($last_key);
}
}
}
$last_keys[$patch_phase] = $full_key;
foreach ($patch['after'] as $after_key => $after) {View on GitHub (pinned to 5720a38cfe)
Solutions
- Add an explicit 'after' key to the first patch: array() if it truly depends on nothing, or the full keys of patches it must follow (e.g. a phabricator patch your schema builds on).
- Every subsequent patch may then omit 'after' and inherit implicit ordering.
- Re-run 'bin/storage status' to confirm the list loads.
Example fix
// before
'20180101.widget.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20180101.widget.sql'),
),
// after
'20180101.widget.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20180101.widget.sql'),
'after' => array(),
), Defensive patterns
Strategy: validation
Validate before calling
// The very first patch of each list must declare 'after' explicitly:
$patches = (new MyApplicationPatchList())->getPatches();
$first = array_slice($patches, 0, 1, true);
foreach ($first as $key => $patch) {
if (!array_key_exists('after', $patch)) {
throw new Exception("First patch '{$key}' must declare an explicit 'after'");
}
} Prevention
- Template the bootstrap patch of a new application with 'after' => array() from day one.
- Later patches may rely on implicit ordering; only the first entry needs the explicit key.
When it happens
Trigger: The first entry of an application's getPatches() return value (in the default phase) has no 'after' key. Typically this is the bootstrap patch of a brand-new application or extension.
Common situations: Creating the first storage patch for a new Phabricator application; splitting an existing patch list into a new namespace whose first patch was written without dependencies.
Related errors
- Patch '%s' references nonexistent dependency, '%s'. Patches
- %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
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/6603d457234cb6a6.
Report an issue: GitHub.