phacility/phabricator · error · Exception

Storage patch "%s" specifies a "@phase" value ("%s"), but it

Error message

Storage patch "%s" specifies a "@phase" value ("%s"), but it already has a specified phase ("%s"). Patches may not specify multiple phases.

What it means

A PHP patch file may declare its phase exactly once. The header parser remembers the phase after the first '// @phase' line; if a second, different '@phase' attribute appears later in the header block, the loader throws because a single patch cannot execute in two phases.

Source

Thrown at src/infrastructure/storage/patch/PhabricatorSQLPatchList.php:312

                    '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)));
              }

              if (isset($attributes['phase'])) {
                throw new Exception(
                  pht(
                    'Storage patch "%s" specifies a "@phase" value ("%s"), '.
                    'but it already has a specified phase ("%s"). Patches '.
                    'may not specify multiple phases.',
                    $patch_name,
                    $phase_name,
                    $attributes['phase']));
              }

              $attributes[$attr_key] = $phase_name;
              break;
            default:
              throw new Exception(
                pht(
                  'Storage patch "%s" specifies attribute "%s", but this '.
                  'attribute is unknown.',
                  $patch_name,
                  $attr_key));

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Keep only one '// @phase' line in the header block and delete the other.
  2. Verify the surviving value is the intended phase ('default' or 'worker').
  3. Re-run 'bin/storage status' to confirm the file parses.

Example fix

// before
<?php
// @phase worker
// @phase default
// Migrates job tables.

// after
<?php
// @phase worker
// Migrates job tables.
Defensive patterns

Strategy: validation

Validate before calling

// A header block must contain at most one '@phase' line:
$lines = file($patch_path);
$phase_lines = preg_grep('/^\s*\/\/\s*@phase\s+\S+/', $lines);
if (count($phase_lines) > 1) {
  throw new Exception('Patch header declares more than one phase');
}

Prevention

When it happens

Trigger: The leading comment block of a .php patch contains two '// @phase ...' lines — e.g. a merged patch file where both contributors added a phase annotation, or an edited header where the old line was left in place.

Common situations: Merge conflicts in patch headers resolved by keeping both lines; editing an existing '@phase' line by duplicating it and editing the copy instead of the original.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/41cb6fe6d7b413e5. Report an issue: GitHub.