phacility/phabricator · error · Exception
Storage patch "%s" specifies a "@phase" attribute with no ph
Error message
Storage patch "%s" specifies a "@phase" attribute with no phase value. Phase attributes must specify a value, like "@phase default".
What it means
PHP storage patches can declare their execution phase with a header comment such as '// @phase worker'. The attribute parser (getPHPPatchAttributes) scans leading '//' lines; a '@phase' with no value after it is invalid because every phase attribute must name a phase, like '@phase default'.
Source
Thrown at src/infrastructure/storage/patch/PhabricatorSQLPatchList.php:292
// Skip over blank lines.
if (!strlen(trim($line))) {
continue;
}
// If this is a "//" comment...
if (preg_match('(^\s*//)', $line)) {
$matches = null;
if (preg_match('(^\s*//\s*@(\S+)(?:\s+(.*))?\z)', $line, $matches)) {
$attr_key = $matches[1];
$attr_value = trim(idx($matches, 2));
switch ($attr_key) {
case 'phase':
$phase_name = $attr_value;
if (!strlen($phase_name)) {
throw new Exception(
pht(
'Storage patch "%s" specifies a "@phase" attribute with '.
'no phase value. Phase attributes must specify a value, '.
'like "@phase default".',
$patch_name));
}
if (!isset($phase_map[$phase_name])) {
throw new Exception(
pht(
'Storage patch "%s" specifies a "@phase" value ("%s"), '.
'but this is not a recognized phase. Valid phases '.
'are: %s.',
$patch_name,
$phase_name,
implode(', ', $phase_list)));
}
View on GitHub (pinned to 5720a38cfe)
Solutions
- Complete the attribute in the named patch file: '// @phase default' or '// @phase worker'.
- If no phase was intended, delete the '@phase' line entirely (a plain '// ...' comment is fine).
- Re-run 'bin/storage status' to confirm the file parses.
Example fix
// before (top of 20180102.jobs.php) <?php // @phase // Creates job tables. // after <?php // @phase worker // Creates job tables.
Defensive patterns
Strategy: validation
Validate before calling
// Lint PHP patch headers before the storage tooling parses them:
foreach (Filesystem::listDirectory($this->getPatchDirectory()) as $file) {
if (!preg_match('/\.php$/', $file)) { continue; }
$head = implode('', array_slice(file($dir.'/'.$file), 0, 10));
if (preg_match('/^\s*\/\/\s*@phase\s*$/m', $head)) {
throw new Exception("{$file}: '@phase' attribute needs a value");
}
} Prevention
- Finish phase annotations before saving patch files; a bare '// @phase' is a hard error.
- If undecided about the phase, omit the line — absence means the default phase.
When it happens
Trigger: The first comment block of a .php patch file contains '// @phase' with nothing after it (or only whitespace). Parsing runs when the patch list is built, so any 'bin/storage' command hits it.
Common situations: Starting to add a phase annotation, saving, and forgetting to finish it; refactoring a header comment and accidentally deleting the phase name; copy-pasting a header template whose placeholder value was never filled in.
Related errors
- Storage patch "%s" specifies a "@phase" value ("%s"), but th
- Storage patch "%s" specifies a "@phase" value ("%s"), but it
- Storage patch "%s" specifies it should apply in phase "%s",
- Storage patch "%s" executes in phase "%s", but depends on pa
- Storage patch "%s" specifies attribute "%s", but this attrib
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/fe0d22bf95e96dd1.
Report an issue: GitHub.