phacility/phabricator · error · Exception

Unknown patch "%s" in "%s", expected ".php" or ".sql" suffix

Error message

Unknown patch "%s" in "%s", expected ".php" or ".sql" suffix.

What it means

PhabricatorSQLPatchList::buildPatchesFromDirectory() found an entry in a patch directory that does not end in .sql or .php — the only suffixes the patch system accepts. Stray files (notes, backups, subdirectories) break patch discovery, and because the scan runs while building the whole patch map, every storage command fails, not just the one touching that patch.

Source

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

  abstract public function getNamespace();
  abstract public function getPatches();

  /**
   * Examine a directory for `.php` and `.sql` files and build patch
   * specifications for them.
   */
  protected function buildPatchesFromDirectory($directory) {
    $patch_list = Filesystem::listDirectory(
      $directory,
      $include_hidden = false);

    sort($patch_list);
    $patches = array();

    foreach ($patch_list as $patch) {
      $matches = null;
      if (!preg_match('/\.(sql|php)$/', $patch, $matches)) {
        throw new Exception(
          pht(
            'Unknown patch "%s" in "%s", expected ".php" or ".sql" suffix.',
            $patch,
            $directory));
      }

      $patch_type = $matches[1];
      $patch_full_path = rtrim($directory, '/').'/'.$patch;

      $attributes = array();
      if ($patch_type === 'php') {
        $attributes = $this->getPHPPatchAttributes(
          $patch,
          $patch_full_path);
      }

      $patches[$patch] = array(
        'type' => $patch_type,

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Find the offender with `ls <patch-dir> | grep -v -E '\.(sql|php)$'` — the error message already names the file and directory.
  2. Delete the stray file or move it outside the patch directory.
  3. If a file must remain, make it hidden (leading dot) — hidden entries are skipped by the scan.

Example fix

# before: resources/sql/patches/ contains notes.txt
mv resources/sql/patches/notes.txt .

# after: only .sql/.php entries remain, the patch map builds again
./bin/storage status
Defensive patterns

Strategy: validation

Validate before calling

find resources/sql/patches -mindepth 1 -maxdepth 1 \
  ! -name '.*' ! -name '*.sql' ! -name '*.php' -print -quit \
  | grep -q . && { echo 'stray file in patch directory' >&2; exit 1; }

Prevention

When it happens

Trigger: Any non-hidden file or directory in a patch directory whose name fails to match /\.(sql|php)$/: for example notes.txt, foo.sql.orig, a .bak file, or a subdirectory. Hidden dotfiles are excluded, so .gitignore is safe.

Common situations: A developer leaving notes inside resources/sql/patches; editor or rsync backups dropped next to patch files; extracting an archive into the patches directory; adding a data file a PHP patch should read.

Related errors


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