ory/hydra · error

unknown migration direction %q

Error message

unknown migration direction %q

What it means

MigrationBox.SelectedMigrations(direction, dialect, fallbacks...) only accepts "up" or "down" (oryx/popx/migration_box.go:249). Any other direction string returns this error instead of returning an empty list, catching programmer mistakes early.

Source

Thrown at oryx/popx/migration_box.go:249

		return []string{dbal.DriverPostgreSQL}
	}
	return nil
}

// SelectedMigrations returns a copy of the migrations selected for the
// connection's dialect and direction. It uses the same exact-match and fallback
// ranking as Up, Down, and Status so callers can audit the effective migration
// set without duplicating filename-selection logic.
func (mb *MigrationBox) SelectedMigrations(direction string) (Migrations, error) {
	dialect := mb.c.Dialect.Name()
	fallbacks := mb.migrationFallbacks()
	switch direction {
	case "up":
		return slices.Clone(mb.migrationsUp.sortAndFilter(dialect, fallbacks...)), nil
	case "down":
		return slices.Clone(mb.migrationsDown.sortAndFilter(dialect, fallbacks...)), nil
	default:
		return nil, errors.Errorf("unknown migration direction %q", direction)
	}
}

// noTxDDL reports whether the dialect cannot run DDL inside a transaction, so
// both migrations and the migration-status table setup must run in autocommit
// mode. CockroachDB and MySQL auto-commit each DDL statement; YugabyteDB
// restricts DDL inside transactions. Because there is no surrounding
// transaction to roll back a partial failure, new YugabyteDB migrations must
// contain one idempotent statement per file. YugabyteDB also inherits older
// PostgreSQL migration files that predate this rule; service-level guard tests
// track that replay risk, and a dedicated .yugabyte. override is required when
// a new unsafe inherited migration appears.
func (mb *MigrationBox) noTxDDL() bool {
	switch mb.c.Dialect.Name() {
	case dbal.DriverCockroachDB, dbal.DriverMySQL, dbal.DriverYugabyteDB:
		return true
	}
	return false

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Pass exactly "up" or "down" (lowercase) as the direction argument
  2. Normalize/trim input strings before calling SelectedMigrations
  3. Check the calling code for a swapped or mistyped variable

Example fix

// before
mfs, err := mb.SelectedMigrations(direction, dialect)
// after
dir := strings.ToLower(strings.TrimSpace(direction))
if dir != "up" && dir != "down" { return fmt.Errorf("invalid direction %q", direction) }
mfs, err := mb.SelectedMigrations(dir, dialect)
Defensive patterns

Strategy: validation

Validate before calling

func validDirection(d string) bool { return d == "up" || d == "down" }
if !validDirection(direction) { return fmt.Errorf("direction must be up or down, got %q", direction) }

Try / catch

mfs, err := mb.SelectedMigrations(direction, dialect)
if err != nil {
    if strings.Contains(err.Error(), "unknown migration direction") {
        return fmt.Errorf("bad direction %q: use \"up\" or \"down\"", direction)
    }
    return err
}

Prevention

When it happens

Trigger: Calling SelectedMigrations with a direction other than "up" or "down" — e.g. "Up", "", "apply", or a variable that was meant to hold a direction but holds something else.

Common situations: Passing user/config input straight into SelectedMigrations; case mistakes ("UP"); using a status or mode string where a direction string was expected.

Understand the failure class

Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.

Related errors


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