| title | Troubleshoot mssql-python |
|---|---|
| description | Diagnose and resolve common issues when using the mssql-python driver to connect to SQL Server. |
| author | dlevy-msft-sql |
| ms.author | dlevy |
| ms.date | 07/17/2026 |
| ms.service | sql |
| ms.subservice | connectivity |
| ms.topic | troubleshooting |
| ai-usage | ai-assisted |
Diagnose and resolve common issues when using the mssql-python driver to connect to SQL Server, Azure SQL Database, Azure SQL Managed Instance, and SQL database in Microsoft Fabric.
Symptoms:
error: Microsoft Visual C++ 14.0 or greater is required
ERROR: Failed building wheel for mssql-python
Possible causes and solutions:
-
No prebuilt wheel for your platform
- Check that you're running a supported Python version (3.10 and later versions) and platform. See Support lifecycle for the compatibility matrix. Upgrade pip before installing with
pip install --upgrade pip. For repeatable team environments, use the locked workflow in Repeatable deployments or the container patterns in Container and local development to reduce local machine drift.
- Check that you're running a supported Python version (3.10 and later versions) and platform. See Support lifecycle for the compatibility matrix. Upgrade pip before installing with
-
Virtual environment not activated
- Activate your virtual environment first. Installing into the system Python can cause permission errors or conflicts.
python -m venv .venv .venv\Scripts\activate pip install mssql-python
python -m venv .venv source .venv/bin/activate pip install mssql-python
-
Missing Linux system libraries
- The driver requires a small set of system libraries on Linux. See Platform-specific dependencies for the packages to install.
Symptoms:
Import errors or unexpected behavior after installing mssql-python alongside pyodbc in the same environment.
Fix:
mssql-python and pyodbc can coexist. If you see conflicts, create a clean virtual environment:
python -m venv .venv --clear
.venv\Scripts\activate
pip install mssql-pythonpython -m venv .venv --clear
source .venv/bin/activate
pip install mssql-pythonSymptoms:
OperationalError: [08001] (0) Client unable to establish connection
Possible causes and solutions:
-
Server not reachable
- Verify the server name and port are correct.
- Check network connectivity:
ping servernameortelnet servername 1433. - Ensure firewall allows outbound connections on port 1433.
-
SQL Server not running
- Verify SQL Server service is started.
- For named instances, verify SQL Server Browser service is running.
-
Azure SQL firewall rules
- Add your client IP to the Azure SQL firewall rules in the Azure portal.
- For Azure SQL Managed Instance, ensure you're connecting from an allowed network.
# Test basic connectivity
import socket
try:
sock = socket.create_connection(("<server>.database.windows.net", 1433), timeout=5)
print("TCP connection successful")
sock.close()
except Exception as e:
print(f"Cannot reach server: {e}")Symptoms:
OperationalError: [28000] (18456) Login failed for user 'username'.
Possible causes and solutions:
-
Authentication mode mismatch
- For Azure SQL Database, Azure SQL Managed Instance, and SQL database in Fabric, prefer a Microsoft Entra mode such as
Authentication=ActiveDirectoryDefault. - If you're using SQL authentication intentionally, verify that the server allows it and that you're using the correct login format for that endpoint.
- For Azure SQL Database, Azure SQL Managed Instance, and SQL database in Fabric, prefer a Microsoft Entra mode such as
-
Incorrect SQL authentication credentials
- Verify username and password.
- For Azure SQL, include the full username:
username@servername.
-
User doesn't exist in database
- Verify the user has access to the specified database.
- Check if the sign-in is mapped to a database user.
-
Authentication not configured
- Use Microsoft Entra authentication (recommended):
Authentication=ActiveDirectoryDefault. - If you're troubleshooting a local SQL Server that should accept SQL authentication, verify that SQL Server uses mixed mode authentication.
- Use Microsoft Entra authentication (recommended):
Symptoms:
OperationalError: [HYT00] (0) Timeout expired
OperationalError: [HYT01] (0) Connection timeout expired
Possible causes and solutions:
-
Server is slow to respond
- Increase the connection timeout:
conn = mssql_python.connect(connection_string, timeout=60)
-
Network latency
- Check network path to server.
- Consider using a shorter network path or VPN.
-
Server under heavy load
- Try connecting during off-peak hours.
- Contact your database administrator.
Symptoms:
OperationalError: [08001] SSL Provider: The certificate chain was issued by an authority that is not trusted
Solutions:
First, prefer a trusted certificate or the local development patterns in Container and local development. Use TrustServerCertificate=yes only for local development against a server that you control.
For development and testing with a self-signed certificate:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
"TrustServerCertificate=yes;" # Don't use in production
)Caution
TrustServerCertificate=yes is a local-only fallback. Don't carry it into shared devcontainers, CI pipelines, or production deployments. For broader guidance, see Encryption and certificates.
For production, ensure proper certificates are installed and use:
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
"HostnameInCertificate=<server>.domain.com;"
)Symptoms:
ProgrammingError: [42S02] (208) Invalid object name 'TableName'.
Possible causes and solutions:
-
Wrong database context
# Ensure you're connected to the correct database cursor.execute("SELECT DB_NAME()") print(cursor.fetchone()[0])
-
Schema not specified
# Use fully qualified name cursor.execute("SELECT * FROM dbo.TableName")
-
Table doesn't exist
# Check if table exists cursor.execute(""" SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TableName' """)
Symptoms:
ProgrammingError: [42000] (102) Incorrect syntax near '...'.
Solutions:
-
Test SQL in SSMS first to verify syntax
-
Check string escaping - use parameterized queries:
# Wrong - vulnerable to syntax issues and SQL injection cursor.execute(f"SELECT * FROM Production.Product WHERE Name = '{name}'") # Correct - use parameters cursor.execute("SELECT * FROM Production.Product WHERE Name = %(name)s", {"name": name})
Symptoms:
ProgrammingError: [07001] Wrong number of parameters
Solutions:
-
Count placeholders and parameters - they must match
-
Choose the right parameter style:
# Qmark style - positional cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Name LIKE ?", (1, "Adjustable%")) print(cursor.fetchone()) # Pyformat style - named cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(id)s AND Name LIKE %(name)s", {"id": 1, "name": "Adjustable%"}) print(cursor.fetchone())
Symptoms:
DataError: [22007] Invalid datetime format
Solutions:
Use Python datetime objects instead of strings:
from datetime import datetime
cursor.execute("CREATE TABLE #Events (EventDate DATETIME)")
# Wrong - this raises an error for invalid dates
try:
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": "2024-13-45"})
except Exception as e:
print(f"Expected error: {e}")
# Correct - use Python datetime objects
cursor.execute("INSERT INTO #Events (EventDate) VALUES (%(event_date)s)", {"event_date": datetime(2024, 3, 15)})
cursor.execute("SELECT EventDate FROM #Events")
print(cursor.fetchone())Symptoms:
Numbers appear truncated or rounded incorrectly.
Solutions:
Use decimal.Decimal for precise numeric values:
from decimal import Decimal
cursor.execute("CREATE TABLE #PriceDemo (ListPrice DECIMAL(10,2))")
# Preserve full precision
cursor.execute(
"INSERT INTO #PriceDemo (ListPrice) VALUES (%(list_price)s)",
{"list_price": Decimal("19.99")}
)Symptoms:
Special characters appear garbled or cause errors.
Solutions:
-
Use NVARCHAR columns for Unicode data in your database
-
Pass strings directly - the driver handles encoding:
cursor.execute("CREATE TABLE #UnicodeDemo (Name NVARCHAR(50))") cursor.execute("INSERT INTO #UnicodeDemo (Name) VALUES (%(name)s)", {"name": "日本語"}) cursor.execute("SELECT Name FROM #UnicodeDemo") print(cursor.fetchone())
Possible causes and solutions:
-
Missing indexes: Check query execution plan in SSMS.
-
Large result sets: Use
fetchmany()instead offetchall():cursor.arraysize = 1000 while True: rows = cursor.fetchmany() if not rows: break process_rows(rows)
-
Connection pooling disabled: Enable pooling:
import mssql_python mssql_python.pooling(max_size=20, idle_timeout=300)
Symptoms:
Python process runs out of memory.
Solutions:
-
Stream results instead of loading all into memory:
cursor.execute("SELECT * FROM LargeTable") for row in cursor: # Iterates one row at a time process_row(row)
-
Use server-side pagination:
page_size = 1000 offset = 0 while True: cursor.execute( "SELECT * FROM LargeTable ORDER BY ID " "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY", (offset, page_size) ) rows = cursor.fetchall() if not rows: break process_rows(rows) offset += page_size
Temp tables (#tablename) created inside a transaction disappear when the transaction is rolled back. This is a common source of confusion when autocommit is off (the default):
conn = mssql_python.connect(connection_string) # autocommit=False by default
cursor = conn.cursor()
cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
# If the connection rolls back (explicit or on error), #TempData disappears
conn.rollback()
# This fails: Invalid object name '#TempData'
cursor.execute("SELECT * FROM #TempData")Fix: Commit immediately after creating a temp table, or use autocommit mode:
cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
conn.commit() # Lock in the table definition
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
conn.commit()DDL statements that require autocommit mode, such as CREATE DATABASE, fail inside an open transaction. Set autocommit before running them:
conn.autocommit = True
cursor.execute("CREATE DATABASE TestDB")
conn.autocommit = FalseSymptoms:
Data changes don't persist after closing connection.
Solution:
With autocommit=False (default), you must call commit():
cursor.execute("CREATE TABLE #Products (Name NVARCHAR(100))")
cursor.execute("INSERT INTO #Products (Name) VALUES (%(name)s)", {"name": "Widget"})
conn.commit() # Don't forget this!Or use autocommit mode:
conn = mssql_python.connect(connection_string, autocommit=True)Symptoms:
OperationalError: [40001] (1205) Transaction ... was deadlocked on lock resources with another process
Solution:
Retry logic (see Retry logic) handles the immediate failure, but recurring deadlocks indicate a design problem. To fix the root cause, capture the deadlock graph and analyze which statements and lock types are involved. Common fixes include reordering operations so competing transactions acquire locks in the same sequence, reducing transaction scope, and adding appropriate indexes to reduce lock duration.
For a full walkthrough of deadlock analysis, see Deadlocks guide. If you're using Azure SQL Database, see Analyze and prevent deadlocks.
Symptoms:
RuntimeError: CHECK constraint ... Conflict occurred in database ...
RuntimeError: Cannot insert duplicate key ... violation of PRIMARY KEY constraint
Cause:
Data in your batch violates table constraints (primary key, unique, CHECK, or foreign key).
Fix:
Validate data before loading. For large datasets, load into a staging table first, then merge into the target:
# Load into staging, then validate
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(100))")
cursor.bulkcopy("##Staging", rows)
# Check for duplicates before merging
cursor.execute("""
SELECT s.ID FROM ##Staging s
INNER JOIN dbo.Target t ON s.ID = t.ID
""")
dupes = cursor.fetchall()
if dupes:
print(f"Skipping {len(dupes)} duplicate rows")
# Insert only non-duplicate rows
cursor.execute("""
INSERT INTO dbo.Target (ID, Name)
SELECT s.ID, s.Name FROM ##Staging s
WHERE NOT EXISTS (SELECT 1 FROM dbo.Target t WHERE t.ID = s.ID)
""")
conn.commit()For upsert patterns with staging tables, see Data loading and movement patterns.
Symptoms:
RuntimeError: Bulk copy failure - column count mismatch
Cause:
The number of columns in your data doesn't match the target table's column count, or columns are in the wrong order.
Fix:
Ensure your data matches the table schema exactly in order and count:
# Check the target table schema
cursor.execute("""
SELECT COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'MyTable'
ORDER BY ORDINAL_POSITION
""")
for col in cursor.fetchall():
print(col)
# Match your data to the column order
rows = [
(1, "Widget", Decimal("19.99")), # Must match table column order
(2, "Gadget", Decimal("29.99")),
]
cursor.bulkcopy("dbo.MyTable", rows)Symptoms:
Data loads but values are truncated, rounded, or incorrect.
Cause:
Python values don't map cleanly to the target column types. Common cases: float values loaded into decimal columns (precision loss), or oversized strings loaded into fixed-length columns.
Fix:
Use the correct Python types that match your schema:
from decimal import Decimal
# Use Decimal for decimal/numeric columns, not float
rows = [
(1, "Widget", Decimal("19.99")), # Correct
# (1, "Widget", 19.99), # Avoid: float loses precision
]
cursor.bulkcopy("dbo.Products", rows)Symptoms:
Parameters silently fail or raise data type errors when using numpy integer or float types.
Cause:
Numpy types like numpy.int64 and numpy.int32 don't pass isinstance(x, int) in NumPy 2.x. The driver's type inference doesn't recognize them, which causes unexpected behavior.
Fix:
Convert numpy values to native Python types before binding:
import numpy as np
# Convert individual values
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = %(product_id)s", {"product_id": int(np.int64(42))})
# Convert DataFrame values
for _, row in df.iterrows():
cursor.execute(
"INSERT INTO #Orders (ProductID, Qty) VALUES (%(product_id)s, %(qty)s)",
{"product_id": int(row["ProductID"]), "qty": int(row["Qty"])}
)For larger datasets, use the Arrow or pandas integration paths instead, which handle type conversion internally.
Symptoms:
cursor.bulkcopy("#TempTable", data) raises RuntimeError: Invalid object name '#TempTable'.
Cause:
bulkcopy() can't resolve session temp tables (#tablename) due to metadata lookup limitations. Global temp tables (##tablename) and permanent tables work.
Fix:
Use a global temp table or a regular staging table:
# Global temp table (visible to all sessions, dropped when last session disconnects)
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("##Staging", rows)
# Or use a permanent staging table
cursor.execute("CREATE TABLE dbo.Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("dbo.Staging", rows)For small datasets where a session temp table is preferred, use executemany() instead:
cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
cursor.executemany("INSERT INTO #Staging (ID, Name) VALUES (?, ?)", rows)Symptoms:
ImportError: libltdl.so.7: cannot open shared object file: No such file or directory
ImportError: libkrb5.so.3: cannot open shared object file
Fix:
Install the required system packages. The packages differ by distribution:
| Distribution | Install command |
|---|---|
| Ubuntu / Debian | sudo apt-get install libltdl7 libkrb5-3 libgssapi-krb5-2 |
| Red Hat / Fedora | sudo dnf install libtool-ltdl krb5-libs |
| Alpine | apk add libltdl krb5-libs |
For Dockerfile examples, see Container and local development.
Symptoms:
SSL-related errors when connecting from macOS, especially on Apple Silicon.
Fix:
Install OpenSSL via Homebrew and set the linker flags:
brew install openssl
export LDFLAGS="-L/opt/homebrew/opt/openssl/lib"
export CPPFLAGS="-I/opt/homebrew/opt/openssl/include"Use mssql_python.setup_logging() to enable comprehensive DEBUG logging for troubleshooting. All driver operations are logged, including SQL statements, parameters, internal ODBC operations, and connection state changes.
import mssql_python
# Enable logging to file (default)
mssql_python.setup_logging()
# Output to stdout (useful for CI/CD and containers)
mssql_python.setup_logging(output='stdout')
# Output to both file and stdout
mssql_python.setup_logging(output='both')
# Custom log file path (must use .txt, .log, or .csv extension)
mssql_python.setup_logging(log_file_path="/var/log/myapp/mssql.log")Log files are written in CSV format and automatically rotate at 512 MB with five backups. Sensitive data like passwords and access tokens are automatically sanitized in log output.
To add your own log entries alongside driver logs, use driver_logger:
from mssql_python.logging import driver_logger
mssql_python.setup_logging()
driver_logger.debug("[App] Starting data processing")
driver_logger.error("[App] Failed to process record")
# Your entries appear in the same file with the same formatCaution
Logging has performance overhead. Enable it only when troubleshooting, not in production by default.
Retrieve the driver version and server details from an active connection:
import mssql_python
conn = mssql_python.connect(connection_string)
# Driver version
print(f"Version: {mssql_python.__version__}")
# Server information
print(f"Server name: {conn.getinfo(mssql_python.SQL_SERVER_NAME)}")
print(f"Database name: {conn.getinfo(mssql_python.SQL_DATABASE_NAME)}")Test whether a connection is still open before attempting operations:
try:
cursor = conn.cursor()
cursor.execute("SELECT 1")
print("Connection is open")
except mssql_python.Error:
print("Connection is closed or broken")| Error | SQLSTATE | Common cause | Quick fix |
|---|---|---|---|
| Client unable to establish connection | 08001 | Server unreachable | Check server name/port |
| Login failed | 28000 | Wrong credentials | Verify username/password |
| Timeout expired | HYT00/HYT01 | Slow network | Increase timeout |
| Invalid object name | 42S02 | Wrong table/schema | Use fully qualified names |
| Syntax error | 42000 | SQL error | Use parameterized queries |
| Constraint violation | 23000 | FK/PK violation | Check data integrity |
| Deadlock | 40001 | Lock contention | Retry, then analyze the deadlock graph |