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

  1. 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.
  2. If the extra dot is a typo or stray join artifact, fix the construction of the string.
  3. 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

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


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