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
- Use at most two parts: "schema_name"."table_name" (or just "table_name") for MigrationsTable
- Drop MigrationsTableQuoted (leave false) if you don't actually need quoted/case-sensitive identifiers
- 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
- Remember Postgres identifiers are schema.table at most — never include a database/catalog part
- Only enable MigrationsTableQuoted for genuinely case-sensitive or reserved-word identifiers
- Test quoted-identifier config with the actual Postgres version before rollout
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
- no config
- no schema
- no database name
- database is dirty
- unable to parse option x-migrations-table-quoted: %w
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/9aeda5d547be98fd.
Report an issue: GitHub.