payloadcms/payload · critical · Error
No previous migration schema file found! A prior migration f
Error message
No previous migration schema file found! A prior migration from v2 is required to migrate to v3.
What it means
Thrown by the Postgres v2→v3 migration when no prior Drizzle snapshot JSON file is found in the migration directory. The v2→v3 migration diffs the previous snapshot against the current schema to generate the column moves (relationships/uploads out of the join table), so without a baseline snapshot there is nothing to diff and the migration cannot run.
Source
Thrown at packages/drizzle/src/postgres/predefinedMigrations/v2-v3/index.ts:64
* @param req
*/
export const migratePostgresV2toV3 = async ({ debug, payload, req }: Args) => {
const adapter = payload.db as unknown as BasePostgresAdapter
const dir = payload.db.migrationDir
// get the drizzle migrateUpSQL from drizzle using the last schema
const { generateDrizzleJson, generateMigration, upSnapshot } = adapter.requireDrizzleKit()
const drizzleJsonAfter = generateDrizzleJson(adapter.schema) as DrizzleSnapshotJSON
// Get the previous migration snapshot
const previousSnapshot = fs
.readdirSync(dir)
.filter((file) => file.endsWith('.json') && !file.endsWith('relationships_v2_v3.json'))
.sort()
.reverse()?.[0]
if (!previousSnapshot) {
throw new Error(
`No previous migration schema file found! A prior migration from v2 is required to migrate to v3.`,
)
}
let drizzleJsonBefore = JSON.parse(
fs.readFileSync(`${dir}/${previousSnapshot}`, 'utf8'),
) as DrizzleSnapshotJSON
if (upSnapshot && drizzleJsonBefore.version < drizzleJsonAfter.version) {
drizzleJsonBefore = upSnapshot(drizzleJsonBefore)
}
const generatedSQL = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
if (!generatedSQL.length) {
payload.logger.info(`No schema changes needed.`)
process.exit(0)
}View on GitHub (pinned to 00c58b35c0)
Solutions
- Ensure the project has a prior v2-era migration with its generated `.json` snapshot in the migration directory before running the v3 upgrade.
- Generate a baseline migration on the v2 codebase first (`payload migrate:create`), commit the snapshot, then run the v2→v3 migration.
- Confirm `payload.db.migrationDir` resolves to the directory containing the historical snapshots.
Example fix
// before: migrationDir has no *.json snapshot -> throws // after: on the v2 branch, generate the baseline first // payload migrate:create baseline // commit the produced .json snapshot, then run the v2->v3 migration
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'fs'
const snapshot = fs.readdirSync(payload.db.migrationDir)
.filter(f => f.endsWith('.json') && !f.endsWith('relationships_v2_v3.json'))
.sort().reverse()[0]
if (!snapshot) {
throw new Error('No v2 snapshot found; generate baseline migrations on the v2 branch first.')
}
await migratePostgresV2toV3({ payload }) Type guard
null
Try / catch
try {
await migratePostgresV2toV3({ payload })
} catch (err) {
if (/No previous migration schema file found/.test(String(err?.message))) {
payload.logger.error('Generate a v2 baseline migration snapshot before upgrading.')
}
throw err
} Prevention
- Generate and commit migration snapshots on each Payload version.
- Keep the migration directory intact across version upgrades.
- Run v2->v3 only after a valid v2 migration baseline exists.
When it happens
Trigger: Invoking `migratePostgresV2toV3` in a project whose migration directory contains no `.json` snapshot files (other than `relationships_v2_v3.json`, which is explicitly excluded), e.g. a project that never generated v2 migrations, or whose migration directory is empty/misconfigured.
Common situations: Attempting v2→v3 on a fresh project with no migration history; migration directory cleared or pointing at the wrong path; project that used `push`/auto-sync instead of generated migrations; the only JSON present is the v2-v3 relationships file.
Related errors
- Migration ${migration.name} not found locally.
- Migration ${migration.name} not found locally.
- Migration ${migration.name} not found locally.
- Either collection or globalSlug must be passed.
- Either collectionSlug or globalSlug must be provided
AI-assisted analysis of payloadcms/payload@00c58b35c0 (2026-08-12).
Data as JSON: /api/errors/f29d7513e28bca53.
Report an issue: GitHub.