Error message
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Basic Constraints of CA cert not marked critical
Only clients that enable strict checking print this: Python 3.13 and later by default, and openssl verify -x509_strict. Python adds a source location such as "(_ssl.c:1082)".
Python 3.13+ ssl module — VERIFY_X509_STRICT in create_default_context(), X509_V_ERR_CA_BCONS_NOT_CRITICAL
What it means
Python 3.13+ verifies with OpenSSL's strict X.509 mode, which enforces RFC 5280's rule that a CA certificate's basicConstraints extension must be marked critical. The CA in your chain has basicConstraints CA:TRUE without the critical flag, so OpenSSL rejects the chain even though the CA is trusted.
Why it happens
The CA was created from a profile without "critical"
commonAn OpenSSL config section with basicConstraints = CA:TRUE (instead of critical,CA:TRUE), or a CA tool with a non-compliant template, produces such CA certificates.
The client moved to Python 3.13 or newer
commonEarlier Python versions did not set VERIFY_X509_STRICT, so the same CA worked until the upgrade.
An intermediate CA in the chain has the defect
occasionalThe check applies to every CA certificate in the path, including intermediates issued by a compliant root.
How to fix it
- 1.
Find the non-compliant certificate
Print basicConstraints for each CA in the chain; the one without "critical" must be reissued. openssl verify -x509_strict shows all strict-mode problems.
bash openssl x509 -in ca.pem -noout -ext basicConstraints # X509v3 Basic Constraints: <- no "critical" here is the problem # CA:TRUE openssl verify -x509_strict -CAfile ca.pem server.pem - 2.
Reissue the CA with critical basicConstraints
Re-sign the CA certificate with basicConstraints = critical, CA:TRUE and a keyUsage extension. Keeping the same key and subject lets existing certificates verify against it.
bash openssl req -x509 -new -key ca.key -days 3650 -subj "/CN=Example Private CA" \ -addext "basicConstraints=critical,CA:TRUE" \ -addext "keyUsage=critical,keyCertSign,cRLSign" \ -out ca.pem - 3.
Temporary workaround: clear VERIFY_X509_STRICT
Restores the pre-3.13 behaviour for one context while the CA is fixed. Verification of the chain, validity dates and host name stays on.
python # Temporary workaround until the CA is reissued (chain, dates and name are still checked) import ssl ctx = ssl.create_default_context(cafile="ca.pem") ctx.verify_flags &= ~ssl.VERIFY_X509_STRICT