multica-ai/multica · error
identifier %q has more than one dot; only schema.table is su
Error message
identifier %q has more than one dot; only schema.table is supported
What it means
quoteQualifiedIdentifier accepts exactly one or two dot-separated components (table or schema.table). Names with more than one dot are rejected outright instead of being silently sanitized into a legal-but-surprising "schema"."b.c" reference, which would point somewhere the caller did not intend.
Source
Thrown at server/cmd/migrate/main.go:475
}
// quoteQualifiedIdentifier safely quotes either an unqualified table
// name ("foo") or a schema-qualified name ("schema.foo") for embedding
// into a SQL statement. Postgres does not let parametrized queries
// supply identifiers, so we have to interpolate, but pgx.Identifier
// does the right escaping (double-quotes, embedded-quote handling).
//
// The accepted shape is exactly one or two dot-separated components.
// Names containing more than one dot are rejected outright rather than
// silently sanitized into a "schema"."b.c" reference, which is valid
// SQL but almost certainly not what the caller meant.
func quoteQualifiedIdentifier(name string) (string, error) {
if name == "" {
return "", fmt.Errorf("empty identifier")
}
parts := strings.Split(name, ".")
if len(parts) > 2 {
return "", fmt.Errorf("identifier %q has more than one dot; only schema.table is supported", name)
}
for _, p := range parts {
if p == "" {
return "", fmt.Errorf("empty component in %q", name)
}
}
return pgx.Identifier(parts).Sanitize(), nil
}
View on GitHub (pinned to 2c0912b6ec)
Solutions
- Reduce the name to at most schema.table — drop the database/catalog component; in Postgres the database is chosen by connection, not by the identifier.
- If the extra dot is a typo or stray join artifact, fix the construction of the string.
- If you genuinely need dots inside an identifier, pass the pre-quoted form the function was designed to avoid — instead, quote each component yourself once and stop using this helper for that case.
Example fix
// before
q, err := quoteQualifiedIdentifier("mydb.public.issues")
// after
q, err := quoteQualifiedIdentifier("public.issues") Defensive patterns
Strategy: validation
Validate before calling
// Normalize before quoting: strip a leading database/catalog component
// if your tooling conventionally carries one.
func normalizeIdent(name string) string {
parts := strings.Split(name, ".")
if len(parts) > 2 {
parts = parts[len(parts)-2:] // keep schema.table
}
return strings.Join(parts, ".")
} Type guard
func isSupportedIdentifier(name string) bool {
parts := strings.Split(name, ".")
return len(parts) <= 2
} Try / catch
q, err := quoteQualifiedIdentifier(name)
if err != nil {
return fmt.Errorf("%q must be table or schema.table: %w", name, err)
} Prevention
- Standardize on schema.table in configs; the database is implied by the connection.
- When porting MySQL/SQLServer tooling, add a normalization step that drops the catalog component.
- Log the raw value in the error path — the %q in the message already shows it.
When it happens
Trigger: Passing a three-plus-part name such as "db.schema.table", a database-qualified Postgres name, or a value containing stray dots (e.g. a filename fragment or an untrimmed dotted string).
Common situations: Porting tooling from MySQL/SQLServer where db.schema.table is normal; pasting a fully-qualified name from a GUI client; a value built by joining config parts with "." and accidentally including an extra segment.
Related errors
- empty identifier
- empty component in %q
- scan task_usage time range: %w
- stamp watermark: %w
- invalid schema migrations table %q: %w
AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15).
Data as JSON: /api/errors/dd8c4c66f106934c.
Report an issue: GitHub.