{"record":{"id":"26c14ddc4d024727","repo":"mem0ai/mem0","slug":"invalid-collection-name-v-r-must-start-with-a","errorCode":null,"errorMessage":"Invalid collection_name: {v!r}. Must start with a letter or underscore and contain only letters, digits, and underscores.","messagePattern":"Invalid collection_name: (.+?)\\. Must start with a letter or underscore and contain only letters, digits, and underscores\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mem0/configs/vector_stores/azure_mysql.py","lineNumber":37,"sourceCode":"    collection_name: str = Field(\"mem0\", description=\"Collection/table name\")\n    embedding_model_dims: int = Field(1536, description=\"Dimensions of the embedding model\")\n    use_azure_credential: bool = Field(\n        False,\n        description=\"Use Azure DefaultAzureCredential for authentication instead of password\"\n    )\n    ssl_ca: Optional[str] = Field(None, description=\"Path to SSL CA certificate\")\n    ssl_disabled: bool = Field(False, description=\"Disable SSL connection (not recommended for production)\")\n    minconn: int = Field(1, description=\"Minimum number of connections in the pool\")\n    maxconn: int = Field(5, description=\"Maximum number of connections in the pool\")\n    connection_pool: Optional[Any] = Field(\n        None,\n        description=\"Pre-configured connection pool object (overrides other connection parameters)\"\n    )\n\n    @field_validator(\"collection_name\")\n    def validate_collection_name(cls, v):\n        if not _VALID_SQL_IDENTIFIER.match(v):\n            raise ValueError(\n                f\"Invalid collection_name: {v!r}. Must start with a letter or underscore and \"\n                \"contain only letters, digits, and underscores.\"\n            )\n        return v\n\n    @model_validator(mode=\"before\")\n    @classmethod\n    def check_auth(cls, values: Dict[str, Any]) -> Dict[str, Any]:\n        \"\"\"Validate authentication parameters.\"\"\"\n        # If connection_pool is provided, skip validation\n        if values.get(\"connection_pool\") is not None:\n            return values\n\n        use_azure_credential = values.get(\"use_azure_credential\", False)\n        password = values.get(\"password\")\n\n        # Either password or Azure credential must be provided\n        if not use_azure_credential and not password:","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/mem0ai/mem0/blob/001c235229be8795e3834520467bd0d661ed8f34/mem0/configs/vector_stores/azure_mysql.py#L19-L55","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Rename the collection to match [A-Za-z_][A-Za-z0-9_]* — e.g. 'memories_test', 'user_42'","Sanitize derived names: re.sub(r'[^A-Za-z0-9_]', '_', name) and prefix an underscore if it starts with a digit","Never build collection names from untrusted raw input without this normalization"],"exampleFix":"# before\n\"config\": {\"host\": H, \"database\": D, \"collection_name\": f\"mem-{user_id}\"}\n\n# after\nimport re\nname = re.sub(r\"[^A-Za-z0-9_]\", \"_\", f\"mem-{user_id}\")\nif name[0].isdigit():\n    name = f\"_{name}\"\n\"config\": {\"host\": H, \"database\": D, \"collection_name\": name}","handlingStrategy":"validation","validationCode":"import re\n\ndef safe_table_name(name: str) -> str:\n    n = re.sub(r\"[^A-Za-z0-9_]\", \"_\", name)\n    if not re.match(r\"^[A-Za-z_]\", n):\n        n = \"_\" + n\n    return n\n\n\"config\": {\"host\": H, \"database\": D, \"collection_name\": safe_table_name(raw)}","typeGuard":"import re\n\n_SQL_IDENT = re.compile(r\"^[A-Za-z_][A-Za-z0-9_]*$\")\n\ndef is_valid_collection_name(v: object) -> bool:\n    return isinstance(v, str) and bool(_SQL_IDENT.match(v))","tryCatchPattern":"try:\n    Memory.from_config(config)\nexcept ValueError as e:\n    if \"Invalid collection_name\" in str(e):\n        raise ConfigError(\"collection_name must match [A-Za-z_][A-Za-z0-9_]*\") from e\n    raise","preventionTips":["Never interpolate raw user/org IDs into collection_name; normalize them first","Note that names valid in other vector stores (dots, hyphens) are invalid here because MySQL treats it as a SQL identifier"],"tags":["configuration","azure-mysql","vector-store","sql-identifier","validation"],"backgroundTag":null,"analyzedSha":"001c235229be8795e3834520467bd0d661ed8f34","analyzedAt":"2026-08-15T01:55:42.685Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}