ory/kratos · error

migrations have not yet been fully applied

Error message

migrations have not yet been fully applied

What it means

During the service bootstrap pipeline (registry_default.go), a boot check queries the persister's MigrationStatus and, if status.HasPending() is true, refuses to continue with this error. It is a safety gate: the schema is not at the expected migration version, so the service would misbehave against a partially migrated database.

Solutions

  1. Run the migration CLI against the configured DSN: 'kratos migrate sql -e DSN up' (or apply the SQL files with your migration tool).
  2. Verify the DSN used for migration matches the DSN the server uses, so you are migrating the right database.
  3. Check 'kratos migrate status -e DSN' to list pending migrations before and after applying.
  4. If the schema is intentionally behind (e.g. replica), point the service at a fully migrated instance.

Example fix

# before (server started with pending migrations)
kratos serve -c kratos.yml

# after (apply migrations first, then serve)
kratos migrate sql -e DSN up
kratos serve -c kratos.yml
Defensive patterns

Strategy: try-catch

Validate before calling

status, err := m.Persister().MigrationStatus(ctx)
if err == nil && status.HasPending() {
    return fmt.Errorf("%d pending migrations; run migrations before serving", len(status.Pending()))
}

Try / catch

// in startup code
if err := run(); err != nil {
    if strings.Contains(err.Error(), "migrations have not yet been fully applied") {
        // trigger your migration job, then retry startup
        runMigrations(dsn)
        return run()
    }
    return err
}

Prevention

When it happens

Trigger: Starting Kratos with a database whose schema migration history has pending migrations — e.g. upgrading to a newer version without running 'kratos migrate sql up', or the migrate command was run partially/against a different database than the one configured in dsn.

Common situations: Version upgrade deployments where migrations were skipped in CI, blue/green setups pointing the new binary at the old schema, and local dev where migrations were applied to one sqlite/postgres instance but the app dsn points at another.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/795d1be45c9abacc. Report an issue: GitHub.

Appendix: source

Thrown at driver/registry_default.go:300

func (m *RegistryDefault) HealthHandler(_ context.Context) *healthx.Handler {
	if m.healthxHandler == nil {
		m.healthxHandler = healthx.NewHandler(m.Writer(), config.Version,
			healthx.ReadyCheckers{
				"database": func(r *http.Request) error {
					return m.PingContext(r.Context())
				},
				"migrations": func(r *http.Request) error {
					if m.migrationStatus != nil && !m.migrationStatus.HasPending() {
						return nil
					}

					status, err := m.Persister().MigrationStatus(r.Context())
					if err != nil {
						return err
					}

					if status.HasPending() {
						return errors.Errorf("migrations have not yet been fully applied")
					}

					m.migrationStatus = status
					return nil
				},
			})
	}

	return m.healthxHandler
}

func (m *RegistryDefault) WithCSRFHandler(c nosurf.Handler) {
	m.nosurf = c
}

func (m *RegistryDefault) CSRFHandler() nosurf.Handler {
	if m.nosurf == nil {
		panic("csrf handler is not set")

View on GitHub (pinned to b86338da04)