payloadcms/payload · error · Error

Migration ${migration.name} not found locally.

Error message

Migration ${migration.name} not found locally.

What it means

Thrown by `migrateDown` (`payload migrate:rollback`) when the `payload-migrations` table records a migration whose name has no matching file in the local migrations directory. Rollback must execute the migration's `down` function, which only exists in the file — so a missing file aborts the rollback. Typically the migration file was deleted, renamed, or the deployed code is behind the DB.

Source

Thrown at packages/payload/src/database/migrations/migrateDown.ts:32

  const { existingMigrations, latestBatch } = await getMigrations({
    payload,
  })

  if (!existingMigrations?.length) {
    payload.logger.info({ msg: 'No migrations to rollback.' })
    return
  }

  payload.logger.info({
    msg: `Rolling back batch ${latestBatch} consisting of ${existingMigrations.length} migration(s).`,
  })

  const latestBatchMigrations = existingMigrations.filter(({ batch }) => batch === latestBatch)

  for (const migration of latestBatchMigrations) {
    const migrationFile = migrationFiles.find((m) => m.name === migration.name)
    if (!migrationFile) {
      throw new Error(`Migration ${migration.name} not found locally.`)
    }

    const start = Date.now()
    const req = await createLocalReq({}, payload)

    try {
      payload.logger.info({ msg: `Migrating down: ${migrationFile.name}` })
      await initTransaction(req)
      const session = payload.db.sessions?.[await req.transactionID!]
      await migrationFile.down({ payload, req, session })
      payload.logger.info({
        msg: `Migrated down:  ${migrationFile.name} (${Date.now() - start}ms)`,
      })
      // Waiting for implementation here
      await payload.delete({
        id: migration.id!,
        collection: 'payload-migrations',
        req,

View on GitHub (pinned to 00c58b35c0)

Solutions

  1. Restore the missing migration file from version control so its `down()` is available.
  2. If the rollback is intentionally impossible, manually remove the row from the `payload-migrations` collection/table.
  3. Keep migration files immutable once applied — never delete applied migrations.

Example fix

# before: migration file deleted, rollback fails
payload migrate:rollback
# after: restore the file, then rollback
git checkout HEAD -- src/migrations/2024_01_01_init.ts
payload migrate:rollback
Defensive patterns

Strategy: validation

Validate before calling

import { readdir } from 'fs/promises'
const local = new Set((await readdir('./src/migrations')).map(stripExt))
const recorded = await payload.find({ collection: 'payload-migrations', limit: 0 })
const missing = recorded.docs.filter((m) => !local.has(m.name))
if (missing.length) throw new Error(`Missing local migration files: ${missing.map((m) => m.name).join(', ')}`)

Try / catch

try {
  await payload.db.migrateDown()
} catch (err) {
  if (/not found locally/.test((err as Error).message)) {
    // restore the file from git or remove the stray DB row, then retry
  }
  throw err
}

Prevention

When it happens

Trigger: Running `payload migrate:rollback` after deleting a migration file from `src/migrations`; deploying code that no longer ships an older migration that the DB still records; renaming a migration file without re-syncing the DB.

Common situations: Squashing/cleaning up migration files mid-project; CI/CD deploy that pruned old migrations; switching branches where the migration set differs.

Related errors


AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12). Data as JSON: /api/errors/ce44a3d8d86c86ef. Report an issue: GitHub.