mem0ai/mem0 · error · ValueError

Invalid collection_name: {v!r}. Must start with a letter or

Error message

Invalid collection_name: {v!r}. Must start with a letter or underscore and contain only letters, digits, and underscores.

What it means

The Azure MySQL vector store validates collection_name (used as the table identifier) against a SQL-identifier regex: it must start with a letter or underscore and contain only letters, digits, and underscores. A failed match raises ValueError at config-parse time, before any DB connection. This exists because the value is interpolated into DDL/SQL for MySQL.

Source

Thrown at mem0/configs/vector_stores/azure_mysql.py:37

    collection_name: str = Field("mem0", description="Collection/table name")
    embedding_model_dims: int = Field(1536, description="Dimensions of the embedding model")
    use_azure_credential: bool = Field(
        False,
        description="Use Azure DefaultAzureCredential for authentication instead of password"
    )
    ssl_ca: Optional[str] = Field(None, description="Path to SSL CA certificate")
    ssl_disabled: bool = Field(False, description="Disable SSL connection (not recommended for production)")
    minconn: int = Field(1, description="Minimum number of connections in the pool")
    maxconn: int = Field(5, description="Maximum number of connections in the pool")
    connection_pool: Optional[Any] = Field(
        None,
        description="Pre-configured connection pool object (overrides other connection parameters)"
    )

    @field_validator("collection_name")
    def validate_collection_name(cls, v):
        if not _VALID_SQL_IDENTIFIER.match(v):
            raise ValueError(
                f"Invalid collection_name: {v!r}. Must start with a letter or underscore and "
                "contain only letters, digits, and underscores."
            )
        return v

    @model_validator(mode="before")
    @classmethod
    def check_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        """Validate authentication parameters."""
        # If connection_pool is provided, skip validation
        if values.get("connection_pool") is not None:
            return values

        use_azure_credential = values.get("use_azure_credential", False)
        password = values.get("password")

        # Either password or Azure credential must be provided
        if not use_azure_credential and not password:

View on GitHub (pinned to 001c235229)

Solutions

  1. Rename the collection to match [A-Za-z_][A-Za-z0-9_]* — e.g. 'memories_test', 'user_42'
  2. Sanitize derived names: re.sub(r'[^A-Za-z0-9_]', '_', name) and prefix an underscore if it starts with a digit
  3. Never build collection names from untrusted raw input without this normalization

Example fix

# before
"config": {"host": H, "database": D, "collection_name": f"mem-{user_id}"}

# after
import re
name = re.sub(r"[^A-Za-z0-9_]", "_", f"mem-{user_id}")
if name[0].isdigit():
    name = f"_{name}"
"config": {"host": H, "database": D, "collection_name": name}
Defensive patterns

Strategy: validation

Validate before calling

import re

def safe_table_name(name: str) -> str:
    n = re.sub(r"[^A-Za-z0-9_]", "_", name)
    if not re.match(r"^[A-Za-z_]", n):
        n = "_" + n
    return n

"config": {"host": H, "database": D, "collection_name": safe_table_name(raw)}

Type guard

import re

_SQL_IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")

def is_valid_collection_name(v: object) -> bool:
    return isinstance(v, str) and bool(_SQL_IDENT.match(v))

Try / catch

try:
    Memory.from_config(config)
except ValueError as e:
    if "Invalid collection_name" in str(e):
        raise ConfigError("collection_name must match [A-Za-z_][A-Za-z0-9_]*") from e
    raise

Prevention

When it happens

Trigger: Passing collection_name values like 'my-table' (hyphen), 'memories.table', '123memories' (leading digit), 'user memories' (space), or any name with quotes/semicolon — including classic SQL-injection-shaped input — in the azure_mysql vector store config.

Common situations: Deriving the table name from user IDs, org names, or free text; reusing collection names valid in other stores (qdrant/chroma allow dots and hyphens) for MySQL; MySQL doesn't allow hyphens unquoted in identifiers, hence the strict rule.

Related errors


AI-assisted analysis of mem0ai/mem0@001c235229 (2026-08-15). Data as JSON: /api/errors/26c14ddc4d024727. Report an issue: GitHub.