gastownhall/beads · error
invalid identifier: %q
Error message
invalid identifier: %q
What it means
ValidateIdentifier enforces the pattern ^[a-zA-Z_][a-zA-Z0-9_]*$ so identifiers are safe to embed unquoted in DDL, preventing syntax errors and SQL injection through database/table names. This error means the name contains characters outside letters, digits, and underscores, or starts with a digit. It is returned by QuoteIdentifier and wrapped by DDL methods.
Source
Thrown at internal/storage/domain/db/ddl.go:20
import (
"context"
"fmt"
"regexp"
)
var validIdentifier = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
const maxIdentifierLength = 64
// ValidateIdentifier checks whether name is safe to use, unquoted, as a
// database or table identifier in this package's DDL statements.
func ValidateIdentifier(name string) error {
if len(name) > maxIdentifierLength {
return fmt.Errorf("identifier too long: %q (max %d chars)", name, maxIdentifierLength)
}
if !validIdentifier.MatchString(name) {
return fmt.Errorf("invalid identifier: %q", name)
}
return nil
}
type DDLSQLRepository interface {
CreateDatabaseIfNotExists(ctx context.Context, database string) error
// CreateDatabase issues a bare CREATE DATABASE (no IF NOT EXISTS) so the
// server arbitrates creation atomically: success proves this call created
// the database; an already-exists error (MySQL 1007) proves it did not.
// The error is returned unmapped (wrapped with %w) so callers can
// classify it against their driver.
CreateDatabase(ctx context.Context, database string) error
UseDatabase(ctx context.Context, database string) error
}
func NewDDLSQLRepository(runner Runner) DDLSQLRepository {
return &ddlSQLRepository{runner: runner}
}View on GitHub (pinned to 71377f2769)
Solutions
- Sanitize the name: replace invalid characters with underscores and ensure it starts with a letter or underscore.
- Call db.ValidateIdentifier(name) before invoking DDL APIs to fail fast.
- Fix the source of the bad name (env var, config, or derived slug).
- Never attempt to pass user-controlled strings with quotes/backticks — rely on the library's validation.
Example fix
// before
err := ddl.CreateDatabaseIfNotExists(ctx, "my-project.dev") // hyphen/dot rejected
// after
name := strings.ReplaceAll(strings.ReplaceAll("my-project.dev", "-", "_"), ".", "_")
if err := db.ValidateIdentifier(name); err != nil { return err }
err := ddl.CreateDatabaseIfNotExists(ctx, name) Defensive patterns
Strategy: validation
Validate before calling
var identRe = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
func checkIdent(name string) error {
if !identRe.MatchString(name) {
return fmt.Errorf("name %q must match [a-zA-Z_][a-zA-Z0-9_]*", name)
}
return db.ValidateIdentifier(name)
} Type guard
func isSafeIdentifier(name string) bool {
return len(name) > 0 && len(name) <= 64 &&
regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`).MatchString(name)
} Try / catch
if err := db.ValidateIdentifier(name); err != nil {
return fmt.Errorf("invalid database name %q: %w", name, err)
}
err := ddl.CreateDatabaseIfNotExists(ctx, name) Prevention
- Normalize names early: replace -, ., /, and spaces with underscores; prefix a letter if the name starts with a digit.
- Reject empty names from env/config before calling DDL APIs.
- Never build identifiers from untrusted input without validation — this check is your SQL-injection guard.
When it happens
Trigger: Calling CreateDatabaseIfNotExists/CreateDatabase/UseDatabase (or QuoteIdentifier) with a name containing hyphens, dots, spaces, slashes, or other non-[A-Za-z0-9_] characters, or a name starting with a digit, or an empty name.
Common situations: Using filesystem-style or DNS-style names (my-project.dev) as database names; empty BD_DB env var; names built from paths or URLs; identifiers copied from other systems that allow quoting.
Related errors
- could not extract a Notion ID from %q
- identifier too long: %q (max %d chars)
- invalid fromRef: %w
- invalid toRef: %w
- invalid ref format: %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/0937a82e58e1c811.
Report an issue: GitHub.