multica-ai/multica · error
create migrations table: %w
Error message
create migrations table: %w
What it means
CREATE TABLE IF NOT EXISTS for the schema_migrations tracking table failed. IF EXISTS-style idempotence only guards against the table already existing properly; errors arise from insufficient CREATE privilege on the schema, an invalid table identifier at the SQL level (after quoting), disk/filesystem issues, or a cancelled context.
Source
Thrown at server/cmd/migrate/main.go:395
// Best-effort explicit unlock on the success path. On error returns
// the defer still runs; on os.Exit error paths in main() it does not,
// but session-level advisory locks are released automatically when
// the connection closes at process exit, so the next runner is never
// permanently blocked.
defer func() {
if _, err := conn.Exec(ctx, "SELECT pg_advisory_unlock($1)", lockKey); err != nil {
slog.Warn("failed to release migration advisory lock", "error", err)
}
}()
// Create migrations tracking table.
if _, err := conn.Exec(ctx, fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
version TEXT PRIMARY KEY,
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
)
`, tableIdent)); err != nil {
return fmt.Errorf("create migrations table: %w", err)
}
existsSQL := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE version = $1)", tableIdent)
insertSQL := fmt.Sprintf("INSERT INTO %s (version) VALUES ($1)", tableIdent)
deleteSQL := fmt.Sprintf("DELETE FROM %s WHERE version = $1", tableIdent)
for _, file := range opts.Files {
version := migrations.ExtractVersion(file)
var exists bool
if err := conn.QueryRow(ctx, existsSQL, version).Scan(&exists); err != nil {
return fmt.Errorf("check migration %q: %w", version, err)
}
if opts.Direction == "up" {
if exists {
fmt.Printf(" skip %s (already applied)\n", version)
continueView on GitHub (pinned to 2c0912b6ec)
Solutions
- Grant the migration role CREATE on the schema (or use the schema owner)
- Create the target schema first if using schema-qualified tracking table names
- Re-run after fixing privileges; the run is resumable
Example fix
-- before: migrate role lacks CREATE GRANT USAGE ON SCHEMA public TO migrate_user; -- insufficient -- after GRANT USAGE, CREATE ON SCHEMA public TO migrate_user;
Defensive patterns
Strategy: validation
Validate before calling
-- verify the role can create tables before running migrations SELECT has_schema_privilege(current_user, 'public', 'CREATE');
Try / catch
if _, err := conn.Exec(ctx, createTableSQL); err != nil {
return fmt.Errorf("create migrations table: %w", err)
} Prevention
- Run migrations as the schema owner or a role with CREATE
- Create custom target schemas before referencing them in the tracking-table name
- Re-run after fixing privileges; IF NOT EXISTS keeps it idempotent
When it happens
Trigger: Running the migration role without CREATE on the target schema; the custom -schema-migrations-table targets a schema that does not exist; ctx cancelled right after lock acquisition.
Common situations: Least-privilege DB users missing DDL grants; pointing at a schema-qualified tracking table whose schema was never created.
Related errors
- inspect concurrent index %q: %w
- relation %q exists but is not an index
- drop invalid concurrent index %s: %w
- task_usage_hourly pre-103 hook: %w
- attribution strict-constraint pre-198 hook: %w
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/8c8ce8617504b23a.
Report an issue: GitHub.