phacility/phabricator · error · Exception

Storage patch "%s" specifies it should apply in phase "%s",

Error message

Storage patch "%s" specifies it should apply in phase "%s", but this phase is unrecognized. Valid phases are: %s.

What it means

Storage patches are grouped into execution phases (defined by PhabricatorStoragePatch::getPhaseList(), currently 'default' and 'worker') so critical patches can run before workers restart during an upgrade. A patch that sets a 'phase' key with a value not in that list is a typo or an invented phase, and the loader aborts because ordering across phases cannot be validated.

Source

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

          if ($namespace != 'phabricator') {
            throw new Exception(
              pht(
                "Only patches in the '%s' namespace may contain '%s' keys.",
                'phabricator',
                'legacy'));
          }
        } else {
          $patch['legacy'] = false;
        }

        if (!array_key_exists('phase', $patch)) {
          $patch['phase'] = $default_phase;
        }

        $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.",

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Change the 'phase' value to one of the phases listed in the error message ('default' or 'worker').
  2. If ordinary ordering is fine, delete the 'phase' key entirely — absent means the default phase.
  3. Use PhabricatorStoragePatch::PHASE_DEFAULT / PHASE_WORKER literals rather than raw strings to avoid typos.
  4. Re-run 'bin/storage status' to confirm the list loads.

Example fix

// before
'20180101.daemon.sql' => array(
  'type' => 'sql',
  'name' => $this->getPatchPath('20180101.daemon.sql'),
  'phase' => 'daemon',
),

// after
'20180101.daemon.sql' => array(
  'type' => 'sql',
  'name' => $this->getPatchPath('20180101.daemon.sql'),
  'phase' => PhabricatorStoragePatch::PHASE_WORKER,
),
Defensive patterns

Strategy: validation

Validate before calling

// Validate phase literals against the engine's own list:
$valid_phases = array_fuse(PhabricatorStoragePatch::getPhaseList());
foreach ((new MyApplicationPatchList())->getPatches() as $key => $patch) {
  if (isset($patch['phase']) && !isset($valid_phases[$patch['phase']])) {
    throw new Exception("Patch {$key} has unknown phase '{$patch['phase']}'");
  }
}

Prevention

When it happens

Trigger: A patch spec contains 'phase' => 'something' where 'something' is not exactly 'default' or 'worker' (e.g. 'default ', 'Worker', 'compat', 'setup'). Fires during PhabricatorSQLPatchList::buildAllPatches() on any storage-management command.

Common situations: Guessing phase names when adding a patch to a fork; copying a phase name from an incompatible Phabricator version where the phase list differed; case or whitespace mistakes in the literal.

Related errors


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