golang-migrate/migrate · error

"%s" MigrationsTable contains too many dot characters

Error message

"%s" MigrationsTable contains too many dot characters

What it means

When MigrationsTableQuoted is true, the pgx driver expects MigrationsTable to contain one or two quoted identifiers: "table" or "schema"."table". The regex captures each quoted segment; if it finds more than two (i.e. more than one dot-separated quoted pair), WithInstance rejects the value because a table identifier can have at most schema.table components.

Source

Thrown at database/pgx/pgx.go:136

	if len(config.LockTable) == 0 {
		config.LockTable = DefaultLockTable
	}

	if len(config.LockStrategy) == 0 {
		config.LockStrategy = DefaultLockStrategy
	}

	config.migrationsSchemaName = config.SchemaName
	config.migrationsTableName = config.MigrationsTable
	if config.MigrationsTableQuoted {
		re := regexp.MustCompile(`"(.*?)"`)
		result := re.FindAllStringSubmatch(config.MigrationsTable, -1)
		config.migrationsTableName = result[len(result)-1][1]
		if len(result) == 2 {
			config.migrationsSchemaName = result[0][1]
		} else if len(result) > 2 {
			return nil, fmt.Errorf("\"%s\" MigrationsTable contains too many dot characters", config.MigrationsTable)
		}
	}

	conn, err := instance.Conn(context.Background())

	if err != nil {
		return nil, err
	}

	px := &Postgres{
		conn:   conn,
		db:     instance,
		config: config,
	}

	if err := px.ensureLockTable(); err != nil {
		return nil, err
	}

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Use at most two parts: "schema_name"."table_name" (or just "table_name") for MigrationsTable
  2. Drop MigrationsTableQuoted (leave false) if you don't actually need quoted/case-sensitive identifiers
  3. Remove any database/catalog part — Postgres identifiers are limited to schema.table

Example fix

// before
cfg.MigrationsTable = "\"app\".\"public\".\"schema_migrations\""; cfg.MigrationsTableQuoted = true
// after
cfg.MigrationsTable = "\"public\".\"schema_migrations\""; cfg.MigrationsTableQuoted = true
Defensive patterns

Strategy: validation

Validate before calling

func validateMigrationsTable(cfg *pgx.Config) error {
    if !cfg.MigrationsTableQuoted {
        return nil
    }
    re := regexp.MustCompile(`"([^"]+)"`)
    if n := len(re.FindAllStringSubmatch(cfg.MigrationsTable, -1)); n > 2 {
        return fmt.Errorf("MigrationsTable %q has %d quoted parts; max is schema.table (2)", cfg.MigrationsTable, n)
    }
    return nil
}

Try / catch

drv, err := pgx.WithInstance(db, cfg)
if err != nil && strings.Contains(err.Error(), "contains too many dot characters") {
    return fmt.Errorf("quoted MigrationsTable must be \"schema\".\"table\" or \"table\": %w", err)
}

Prevention

When it happens

Trigger: Calling pgx.WithInstance (or Open, which calls it) with config.MigrationsTableQuoted = true and a MigrationsTable containing three or more quoted parts, e.g. "db"."public"."migrations", or stray quotes like "a"."b"."c" (database/pgx/pgx.go:129-137).

Common situations: Copying a fully-qualified three-part identifier (database.schema.table) from SQL Server style conventions into Postgres config; double-quoting each dot segment; templating that wraps the whole value plus parts in quotes.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02). Data as JSON: /api/errors/9aeda5d547be98fd. Report an issue: GitHub.