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
- Find the offender with `ls <patch-dir> | grep -v -E '\.(sql|php)$'` — the error message already names the file and directory.
- Delete the stray file or move it outside the patch directory.
- 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
- Never keep notes or backups inside patch directories; use dot-names or move them out.
- Add the CI guard above so a stray file fails the build, not the next deploy.
- Treat patch directories as machine-read data, not scratch space.
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
- Failed to open output file "%s" for writing.
- %s argument '%s' is not a valid patch. Use '%s' to show patc
- Storage on host "%s" has not been initialized yet. You must
- Some patches could not be applied: %s
- %s '%s' has a patch '%s' which is not an array.
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/e0ac4b9647408601.
Report an issue: GitHub.