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

  1. Complete the attribute in the named patch file: '// @phase default' or '// @phase worker'.
  2. If no phase was intended, delete the '@phase' line entirely (a plain '// ...' comment is fine).
  3. 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

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


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