SQLAlchemy ↔ PostgreSQL SSL Mode: What Exception Is Raised When Verification Fails?
0 reputation · 12 Apr 2020, 03:53 UTC
0 reputation · 12 Apr 2020, 03:53 UTC
When an application uses SQLAlchemy to connect to a PostgreSQL database via psycopg2, the sslmode parameter controls TLS certificate validation. Setting sslmode='verify-full' should cause the driver to reject connections whose server certificate cannot be verified against the supplied CA bundle.
The goal is to determine how SQLAlchemy propagates any SSL errors back to the application. In practice, the exception type and message that surface depend on the underlying DBAPI, leading to inconsistent error handling across drivers.
Given that SQLAlchemy itself does not perform DNS resolution or certificate validation—delegating both to psycopg2—there is uncertainty around the exact exception hierarchy and how to standardize error handling in a multi-driver environment.
Key unresolved questions:
sslmode='verify-full' fails due to an untrusted certificate?34775 reputation · 12 Apr 2020, 09:21 UTC
When sslmode='verify-full' fails because the server certificate cannot be verified, SQLAlchemy raises sqlalchemy.exc.OperationalError (a subclass of DBAPIError). The original psycopg2.OperationalError is preserved in the .orig attribute of that exception.
sqlalchemy.exc.OperationalErrorpsycopg2.OperationalError (accessible via e.orig)(psycopg2.OperationalError) SSL error: certificate verify failed followed by the OpenSSL reason code.e.orig.pgcode == '08006' (connection_failure)SQLAlchemy does not perform TLS validation itself. It passes sslmode and related parameters directly to psycopg2.connect(). When the handshake fails, psycopg2 raises its own OperationalError; SQLAlchemy catches it and re-raises a wrapped sqlalchemy.exc.OperationalError so that application code can use the driver-agnostic hierarchy (DBAPIError → OperationalError).
SQLAlchemy's documented approach is to catch sqlalchemy.exc.DBAPIError (or its subclasses) and inspect .orig for driver-specific details. There is no built-in utility that maps SSL errors across drivers into a single custom exception. A typical helper looks like:
from sqlalchemy.exc import DBAPIError
def is_ssl_verification_failure(exc: DBAPIError) -> bool:
orig = exc.orig
# psycopg2
if hasattr(orig, 'pgcode') and orig.pgcode == '08006':
return 'certificate verify failed' in str(orig).lower()
# asyncpg, pg8000, etc. — add driver-specific checks here
return False
create_engine('postgresql+psycopg2://...', connect_args={'sslmode': 'verify-full', 'sslrootcert': '/path/to/wrong-ca.pem'})engine.connect() inside a try/except sqlalchemy.exc.OperationalError as e: block.type(e.orig) and e.orig to confirm the wrapped exception and message.Are you using the synchronous psycopg2 driver, or an async driver such as asyncpg? With asyncpg, sslmode is not a native parameter; SSL is configured via an SSLContext passed in connect_args, and the wrapped exception will be sqlalchemy.exc.OperationalError with .orig as an asyncpg exception type (e.g., CannotConnectNowError). The wrapping pattern is the same, but the driver-specific checks differ.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.