phacility/phabricator · error · Exception
%s '%s' has a patch with a numeric key, '%s'. Patches must u
Error message
%s '%s' has a patch with a numeric key, '%s'. Patches must use string keys.
What it means
Every patch in a PhabricatorSQLPatchList must be keyed by a descriptive string (conventionally 'YYYYMMDD.short.name.sql'). String keys become the patch's permanent identity ('namespace:key') used for ordering, dependency tracking, and remembering which patches are already applied. Numeric keys mean the entry has no usable identity, so loading the list fails.
Source
Thrown at src/infrastructure/storage/patch/PhabricatorSQLPatchList.php:102
'phase' => true,
);
foreach ($patch as $pkey => $pval) {
if (empty($valid[$pkey])) {
throw new Exception(
pht(
"%s '%s' has a patch, '%s', with an unknown property, '%s'.".
"Patches must have only valid keys: %s.",
__CLASS__,
get_class($patch_list),
$key,
$pkey,
implode(', ', array_keys($valid))));
}
}
if (is_numeric($key)) {
throw new Exception(
pht(
"%s '%s' has a patch with a numeric key, '%s'. ".
"Patches must use string keys.",
__CLASS__,
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));
}
View on GitHub (pinned to 5720a38cfe)
Solutions
- Give the offending patch a full conventional string key: 'YYYYMMDD.descriptive.name.sql'.
- Make sure the key contains non-digit characters (a dot alone is enough) so PHP keeps it a string.
- Replace any array_merge() on patch arrays with the '+' operator to avoid integer-key renumbering.
- Re-run 'bin/storage status' to verify the list parses.
Example fix
// before
return array(
array( // no key -> numeric key 0
'type' => 'sql',
'name' => $this->getPatchPath('20180101.widget.sql'),
),
);
// after
return array(
'20180101.widget.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20180101.widget.sql'),
),
); Defensive patterns
Strategy: validation
Validate before calling
// Assert every key in your patch map is a non-numeric string:
foreach ((new MyApplicationPatchList())->getPatches() as $key => $spec) {
if (is_numeric($key) || !is_string($key)) {
throw new Exception("Patch key must be a descriptive string: {$key}");
}
} Prevention
- Always key patches as 'YYYYMMDD.descriptive.name.sql' — the dot guarantees a non-integer string key.
- Combine patch arrays with '+' (or foreach), never array_merge(), which renumbers integer keys.
- Run 'bin/storage status' after editing any patch list.
When it happens
Trigger: getPatches() returns an entry with an integer key — most often because the array key was omitted entirely ('array(...)' instead of "'20180101.foo.sql' => array(...)"), because the key is pure digits with no letters (e.g. '0000' or '20180101', which PHP treats as an integer), or because the list was combined with array_merge() which renumbers integer keys.
Common situations: Writing the first patch of a new application and using a bare date as the key; merging patch arrays with array_merge() (use '+' instead so string keys are preserved); refactoring a hand-written list into buildPatchesFromDirectory() output and losing keys.
Related errors
- %s '%s' has a patch, '%s', with an unknown property, '%s'.Pa
- %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
- Only patches in the '%s' namespace may contain '%s' keys.
- 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/e6b939351e55bf82.
Report an issue: GitHub.