{"record":{"id":"154a839567bc7373","repo":"zylon-ai/private-gpt","slug":"mssql-connection-requires-pyodbc-scheme-when-no-dr","errorCode":null,"errorMessage":"MSSQL connection requires pyodbc scheme when no driver specified","messagePattern":"MSSQL connection requires pyodbc scheme when no driver specified","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/tabular/database_query_generator.py","lineNumber":435,"sourceCode":"\n            modified_query = f'CALL {proc_name}({params_with_cursor}); FETCH ALL FROM \"{cursor_name}\";'\n\n            return modified_query\n\n        return query\n\n    def _prepare_connection_string(self) -> None:\n        parsed = urlparse(self.connection_string)\n        params = parse_qs(parsed.query, keep_blank_values=True)\n\n        if self._dialect in [Dialects.TSQL]:\n            # MSSQL specific adjustments\n            if not any(k.lower() == \"encrypt\" for k in params):\n                params[\"Encrypt\"] = [\"yes\" if self.ssl else \"no\"]\n\n            if not any(k.lower() == \"driver\" for k in params):\n                if not parsed.scheme.startswith(\"mssql+pyodbc\"):\n                    raise ValueError(\n                        \"MSSQL connection requires pyodbc scheme when no driver specified\"\n                    )\n                params[\"driver\"] = [\"ODBC Driver 18 for SQL Server\"]\n\n        elif self._dialect in [Dialects.MYSQL]:\n            # MySQL specific adjustments\n            if parsed.scheme == \"mysql\" or parsed.scheme == \"mysql+mysqldb\":\n                parsed = parsed._replace(scheme=\"mysql+pymysql\")\n            elif not parsed.scheme.startswith(\"mysql+pymysql\"):\n                raise ValueError(\n                    \"MySQL connection requires mysql, mysql+mysqldb, or mysql+pymysql scheme\"\n                )\n            if not any(k.lower() == \"charset\" for k in params):\n                params[\"charset\"] = [\"utf8mb4\"]\n\n        flattened = {k: v[0] if len(v) == 1 else v for k, v in params.items()}\n        new_parsed = parsed._replace(query=urlencode(flattened, doseq=True))\n        self.connection_string = str(urlunparse(new_parsed))","sourceCodeStart":417,"sourceCodeEnd":453,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/tabular/database_query_generator.py#L417-L453","documentation":"Raised while preparing an MSSQL (TSQL dialect) connection string: when the URL has no explicit 'driver' query parameter, the code injects 'ODBC Driver 18 for SQL Server' — but only if the scheme starts with mssql+pyodbc. Any other scheme (mssql, mssql+pymssql, ...) cannot accept an ODBC driver parameter, so the code rejects it rather than building a broken connection string.","triggerScenarios":"Supplying a connection string like mssql://user:pass@host/db or mssql+pymssql://... without a ?driver=... parameter while the dialect resolves to TSQL.","commonSituations":"Copy-pasting an ADO or pymssql-style URL from another tool; defaulting encrypt/driver handling assumptions from SQL Server docs; switching a generic mssql:// URL into this component without rewriting the scheme.","solutions":["Use the pyodbc scheme: mssql+pyodbc://user:pass@host:1433/db?driver=ODBC+Driver+18+for+SQL+Server","Or keep your existing mssql:// scheme but add an explicit driver parameter — note non-pyodbc schemes still need the driver to be ODBC-compatible for SQLAlchemy","URL-encode spaces in the driver value or use + (ODBC+Driver+18+for+SQL+Server)"],"exampleFix":"# before\nconnection_string = \"mssql://sa:pass@db:1433/master\"\n# after\nconnection_string = (\n    \"mssql+pyodbc://sa:pass@db:1433/master\"\n    \"?driver=ODBC+Driver+18+for+SQL+Server&Encrypt=yes\"\n)","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse, parse_qs\n\nparsed = urlparse(connection_string)\nparams = parse_qs(parsed.query, keep_blank_values=True)\nhas_driver = any(k.lower() == \"driver\" for k in params)\nif not has_driver and not parsed.scheme.startswith(\"mssql+pyodbc\"):\n    raise ValueError(\n        \"MSSQL URL needs ?driver=... or the mssql+pyodbc scheme\"\n    )","typeGuard":"def is_valid_mssql_url(conn: str) -> bool:\n    p = urlparse(conn)\n    q = parse_qs(p.query, keep_blank_values=True)\n    return p.scheme.startswith(\"mssql+pyodbc\") or any(\n        k.lower() == \"driver\" for k in q\n    )","tryCatchPattern":null,"preventionTips":["Standardize on mssql+pyodbc://…?driver=ODBC+Driver+18+for+SQL+Server in all environments","URL-encode spaces in driver names (+ or %20)"],"tags":["database","mssql","connection-string","pyodbc","configuration"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}