{"record":{"id":"d4dd92d1b5a8b00e","repo":"multica-ai/multica","slug":"invalid-direction-q-want-up-or-down","errorCode":null,"errorMessage":"invalid direction %q (want \\\"up\\\" or \\\"down\\\")","messagePattern":"invalid direction %q \\(want \\\\\"up\\\\\" or \\\\\"down\\\\\"\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/cmd/migrate/main.go","lineNumber":340,"sourceCode":"}\n\n// runMigrations applies (direction=\"up\") or rolls back (direction=\"down\")\n// the given file list against the supplied pool, serialized through a\n// Postgres session-level advisory lock so multiple concurrent runners\n// (multi-replica startup, scale-up, manual migrate overlap) take turns\n// instead of racing each other.\n//\n// It is safe to invoke concurrently from multiple goroutines or\n// processes against the same database with the same options: every\n// caller blocks on pg_advisory_lock, and once it is their turn the\n// already-applied EXISTS check turns each finished migration into a\n// no-op skip. See GitHub multica-ai/multica#3647 / MUL-2923.\nfunc runMigrations(ctx context.Context, pool *pgxpool.Pool, opts runOptions) error {\n\tswitch opts.Direction {\n\tcase \"up\", \"down\":\n\t\t// ok\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid direction %q (want \\\"up\\\" or \\\"down\\\")\", opts.Direction)\n\t}\n\n\ttable := opts.SchemaMigrationsTable\n\tif table == \"\" {\n\t\ttable = defaultSchemaMigrationsTable\n\t}\n\ttableIdent, err := quoteQualifiedIdentifier(table)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid schema migrations table %q: %w\", table, err)\n\t}\n\tlockKey := opts.AdvisoryLockKey\n\tif lockKey == 0 {\n\t\tlockKey = migrationAdvisoryLockKey\n\t}\n\n\t// pg_advisory_lock is scoped to a single session, so we must pin one\n\t// *pgxpool.Conn for the whole run — calling pool.Exec would attach the\n\t// lock to a random connection that pgxpool could hand back out before","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/cmd/migrate/main.go#L322-L358","documentation":"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.","triggerScenarios":"Passing -direction=upward, an empty string from an unset flag/env, or typo like \"Up\" (case-sensitive) when invoking server/cmd/migrate.","commonSituations":"Scripts with a variable that expands to empty; CI job parameter misspelled; calling runMigrations programmatically with an unvalidated string.","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"],"exampleFix":"# before\nmigrate -direction=\"$MIGRATE_DIR\"   # unset -> \"\"\n\n# after\nMIGRATE_DIR=${MIGRATE_DIR:-up}\nmigrate -direction=\"$MIGRATE_DIR\"","handlingStrategy":"validation","validationCode":"switch direction {\ncase \"up\", \"down\":\ndefault:\n    return fmt.Errorf(\"invalid direction %q\", direction)\n}","typeGuard":"func isValidDirection(s string) bool { return s == \"up\" || s == \"down\" }","tryCatchPattern":null,"preventionTips":["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"],"tags":["go","cli","validation","migration"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}