zylon-ai/private-gpt · error · ValueError

MySQL connection requires mysql, mysql+mysqldb, or mysql+pym

Error message

MySQL connection requires mysql, mysql+mysqldb, or mysql+pymysql scheme

What it means

Raised while preparing a MySQL connection string: the component normalizes mysql and mysql+mysqldb schemes to mysql+pymysql (because pymysql is the supported driver), and rejects any other scheme such as mysql+asyncpg, mysql+mysqlconnector, or a bare typo. This guarantees the SQLAlchemy engine is always built on pymysql, which the rest of the tabular query path expects.

Source

Thrown at private_gpt/components/tabular/database_query_generator.py:445

        if self._dialect in [Dialects.TSQL]:
            # MSSQL specific adjustments
            if not any(k.lower() == "encrypt" for k in params):
                params["Encrypt"] = ["yes" if self.ssl else "no"]

            if not any(k.lower() == "driver" for k in params):
                if not parsed.scheme.startswith("mssql+pyodbc"):
                    raise ValueError(
                        "MSSQL connection requires pyodbc scheme when no driver specified"
                    )
                params["driver"] = ["ODBC Driver 18 for SQL Server"]

        elif self._dialect in [Dialects.MYSQL]:
            # MySQL specific adjustments
            if parsed.scheme == "mysql" or parsed.scheme == "mysql+mysqldb":
                parsed = parsed._replace(scheme="mysql+pymysql")
            elif not parsed.scheme.startswith("mysql+pymysql"):
                raise ValueError(
                    "MySQL connection requires mysql, mysql+mysqldb, or mysql+pymysql scheme"
                )
            if not any(k.lower() == "charset" for k in params):
                params["charset"] = ["utf8mb4"]

        flattened = {k: v[0] if len(v) == 1 else v for k, v in params.items()}
        new_parsed = parsed._replace(query=urlencode(flattened, doseq=True))
        self.connection_string = str(urlunparse(new_parsed))

    def _check_connection(self) -> str | None:
        """Check if the database connection can be established.

        Returns None if successful, or an error message if failed.
        """
        try:
            conn = self._ensure_connected()
            dialect = classify_dialect(
                self._engine.dialect.name if self._engine else None

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Use mssql-normalized MySQL form: mysql+pymysql://user:pass@host:3306/db
  2. Plain mysql:// or mysql+mysqldb:// also work (auto-rewritten to pymysql)
  3. Lowercase the scheme and double-check for typos like mysql+pymsql

Example fix

# before
connection_string = "mysql+mysqlconnector://root:pass@db:3306/sakila"
# after
connection_string = "mysql+pymysql://root:pass@db:3306/sakila"
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse

scheme = urlparse(connection_string).scheme.lower()
if scheme not in {"mysql", "mysql+mysqldb", "mysql+pymysql"}:
    raise ValueError(
        "MySQL URL scheme must be mysql, mysql+mysqldb, or mysql+pymysql"
    )

Type guard

def is_valid_mysql_url(conn: str) -> bool:
    scheme = urlparse(conn).scheme.lower()
    return scheme in {"mysql", "mysql+mysqldb", "mysql+pymysql"}

Prevention

When it happens

Trigger: Supplying a connection string whose scheme is not mysql, mysql+mysqldb, or mysql+pymysql while the dialect resolves to MySQL — e.g. mysql+mysqlconnector://... or mariadb://...

Common situations: Reusing a URL written for a different MySQL driver stack; attempting MariaDB URLs; uppercase scheme (MySQL://) which urlparse does not lowercase, failing the exact-match checks.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/9d3bd33b1e012316. Report an issue: GitHub.