ory/hydra · error

migration down: unable count existing migration

Error message

migration down: unable count existing migration

What it means

Before running Down migrations, the migrator counts rows in the schema_migration table to determine how many steps can be reverted. This error wraps a failure of that COUNT query against the migrations table. It means the tracking table could not be read at all.

Solutions

  1. Confirm you are connected to the correct database (check DSN)
  2. Verify the schema_migration table exists; run migrations init/up first if the database was never migrated
  3. Check the wrapped cause for the underlying SQL error and address privileges or connectivity
Defensive patterns

Strategy: validation

Validate before calling

var n int
if err := db.QueryRow("SELECT COUNT(*) FROM schema_migration").Scan(&n); err != nil {
    return fmt.Errorf("schema_migration table not readable; run migrations init/up first: %w", err)
}

Try / catch

if err := box.Down(ctx, 1); err != nil {
    if strings.Contains(err.Error(), "unable count existing migration") {
        // verify DSN and that the DB has been migrated before attempting rollback
    }
    return err
}

Prevention

When it happens

Trigger: Calling MigrationBox.Down when the schema_migration table does not exist (migrations never initialized), is corrupted, or the query fails due to permissions/connection issues.

Common situations: Running Down on a fresh database that never had migrations applied; wrong DSN pointing to an empty database; table renamed manually; revoked SELECT privileges.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/2cd79d74e02fae4b. Report an issue: GitHub.

Appendix: source

Thrown at oryx/popx/migrator.go:345

// Down runs pending "down" migrations and rolls back the
// database by the specified number of steps.
// If step <= 0, all down migrations are run.
func (mb *MigrationBox) Down(ctx context.Context, steps int) (err error) {
	ctx, span := startSpan(ctx, MigrationDownOpName, trace.WithAttributes(attribute.Int("steps", steps)))
	defer otelx.End(span, &err)

	if steps <= 0 {
		steps = math.MaxInt
	}
	remainingSteps := steps

	c := mb.c.WithContext(ctx)
	return errors.WithStack(mb.exec(ctx, func() (err error) {
		mtn := sanitizedMigrationTableName(c)
		count, err := c.Count(mtn)
		if err != nil {
			return errors.Wrap(err, "migration down: unable count existing migration")
		}
		attemptSteps := min(remainingSteps, count)

		mfs := mb.migrationsDown.sortAndFilter(mb.c.Dialect.Name(), mb.migrationFallbacks()...)
		slices.Reverse(mfs)
		if len(mfs) > count {
			// skip all migrations that were not yet applied
			mfs = mfs[len(mfs)-count:]
		}

		reverted := 0
		defer func() {
			migrationsToRevertCount := min(attemptSteps, len(mfs))
			mb.l.Debugf("Successfully reverted %d/%d migrations.", reverted, migrationsToRevertCount)
			if err != nil {
				mb.l.WithError(err).Error("Problem reverting migrations.")
			}
		}()

View on GitHub (pinned to 4174065ffb)