pocketbase/pocketbase · critical
failed to apply migration %s: %w
Error message
failed to apply migration %s: %w
What it means
Thrown when a migration's Up function returns an error while the runner applies it inside a transaction. The migration file name is included. Because it happens inside RunInTransaction, the whole batch's changes for that transaction roll back; the migration is not recorded as applied.
Source
Thrown at core/migrations_runner.go:154
}
shouldReapply, err := m.ReapplyCondition(txApp, r, m.File)
if err != nil {
return err
}
if !shouldReapply {
continue
}
// clear previous history stored entry
// (it will be recreated after successful execution)
r.saveRevertedMigration(txApp, m.File)
}
// ignore empty Up action
if m.Up != nil {
if err := m.Up(txApp); err != nil {
return fmt.Errorf("failed to apply migration %s: %w", m.File, err)
}
}
if err := r.saveAppliedMigration(txApp, m.File); err != nil {
return fmt.Errorf("failed to save applied migration info for %s: %w", m.File, err)
}
applied = append(applied, m.File)
}
return nil
})
})
if err != nil {
return nil, err
}
return applied, nilView on GitHub (pinned to 5d217ddb50)
Solutions
- Read the wrapped error — it is whatever the migration's Up returned, pinpointing the failing statement.
- Make migrations idempotent (IF NOT EXISTS, IF EXISTS) so re-runs and drifted schemas don't break them.
- Fix the DB drift the migration assumed away (e.g. drop the manually created table or column) and re-run.
- Use 'migrate down' for the failing migration only if it has a Down and its changes partially applied outside a transaction context.
Example fix
// before: non-idempotent migration
m.Up = func(db core.App) error {
_, err := db.DB().Exec("CREATE TABLE reports (...)")
return err
}
// after
m.Up = func(db core.App) error {
_, err := db.DB().Exec("CREATE TABLE IF NOT EXISTS reports (...)")
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: verify schema assumptions the migration makes
var n int
app.DB().NewQuery("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='reports'").Row(&n)
if n > 0 && migrationCreatesReports {
return fmt.Errorf("table reports already exists; fix drift before migrating")
} Try / catch
_, err := runner.Up()
if err != nil {
var me *migrationApplyError // if you wrap it; otherwise match the message prefix
if errors.As(err, &me) || strings.Contains(err.Error(), "failed to apply migration") {
// transaction rolled back; fix the wrapped cause (often constraint/drift),
// make the migration idempotent, then re-run migrate up
}
} Prevention
- Write idempotent migrations (IF NOT EXISTS / IF EXISTS).
- Test migrations on a copy of production pb_data before deploying.
- Never let humans hot-fix schemas that migrations will later assume control of.
When it happens
Trigger: Running migrate up where a migration's Go code fails: invalid SQL inside m.Up(txApp), unique constraint violation, table already exists (migration not idempotent when re-applied), or external state the migration depends on being absent.
Common situations: Deploying a new migration to a DB where the schema was already changed by hand; migrations written against an older schema version; data-backfill migrations hitting duplicate keys; SQLite locking during long migrations.
Related errors
- failed to save applied migration info for %s: %w
- Invalid realtime client.
- missing migration file name
- failed to create OTP: %w
- failed to restart the app process: %w
AI-assisted analysis of pocketbase/pocketbase@5d217ddb50 (2026-08-15).
Data as JSON: /api/errors/17b89bfa8e642096.
Report an issue: GitHub.