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)
				continue

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Grant the migration role CREATE on the schema (or use the schema owner)
  2. Create the target schema first if using schema-qualified tracking table names
  3. 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

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


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/8c8ce8617504b23a. Report an issue: GitHub.