multica-ai/multica · error
invalid direction %q (want \"up\" or \"down\")
Error message
invalid direction %q (want \"up\" or \"down\")
What it means
The migrate command rejected its direction option before touching the database: runMigrations only accepts "up" or "down". This is pure input validation on opts.Direction, typically populated from a CLI flag or a programmatic call into the migrate package.
Source
Thrown at server/cmd/migrate/main.go:340
}
// runMigrations applies (direction="up") or rolls back (direction="down")
// the given file list against the supplied pool, serialized through a
// Postgres session-level advisory lock so multiple concurrent runners
// (multi-replica startup, scale-up, manual migrate overlap) take turns
// instead of racing each other.
//
// It is safe to invoke concurrently from multiple goroutines or
// processes against the same database with the same options: every
// caller blocks on pg_advisory_lock, and once it is their turn the
// already-applied EXISTS check turns each finished migration into a
// no-op skip. See GitHub multica-ai/multica#3647 / MUL-2923.
func runMigrations(ctx context.Context, pool *pgxpool.Pool, opts runOptions) error {
switch opts.Direction {
case "up", "down":
// ok
default:
return fmt.Errorf("invalid direction %q (want \"up\" or \"down\")", opts.Direction)
}
table := opts.SchemaMigrationsTable
if table == "" {
table = defaultSchemaMigrationsTable
}
tableIdent, err := quoteQualifiedIdentifier(table)
if err != nil {
return fmt.Errorf("invalid schema migrations table %q: %w", table, err)
}
lockKey := opts.AdvisoryLockKey
if lockKey == 0 {
lockKey = migrationAdvisoryLockKey
}
// pg_advisory_lock is scoped to a single session, so we must pin one
// *pgxpool.Conn for the whole run — calling pool.Exec would attach the
// lock to a random connection that pgxpool could hand back out beforeView on GitHub (pinned to 2c0912b6ec)
Solutions
- Pass exactly up or down: ./migrate -direction=up
- If the value comes from env/CI variables, default it and assert non-empty before invoking
- Check for surrounding whitespace in the flag value
Example fix
# before
migrate -direction="$MIGRATE_DIR" # unset -> ""
# after
MIGRATE_DIR=${MIGRATE_DIR:-up}
migrate -direction="$MIGRATE_DIR" Defensive patterns
Strategy: validation
Validate before calling
switch direction {
case "up", "down":
default:
return fmt.Errorf("invalid direction %q", direction)
} Type guard
func isValidDirection(s string) bool { return s == "up" || s == "down" } Prevention
- Default the direction flag to "up" at the CLI layer
- Validate CI/job parameters before invoking the migrate binary
- Values are case-sensitive and whitespace-sensitive — trim input
When it happens
Trigger: Passing -direction=upward, an empty string from an unset flag/env, or typo like "Up" (case-sensitive) when invoking server/cmd/migrate.
Common situations: Scripts with a variable that expands to empty; CI job parameter misspelled; calling runMigrations programmatically with an unvalidated string.
Related errors
- invalid schema migrations table %q: %w
- --name is required
- --runtime-id is required
- --runtime-config must be valid JSON: %w
- %s must be a JSON object, or 'null' to clear
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/d4dd92d1b5a8b00e.
Report an issue: GitHub.