Error message
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Also printed as
- ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Python appends the C source location, e.g. "(_ssl.c:1082)"; the number changes with the Python version and means nothing for diagnosis. urllib wraps the error as "<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] …>", requests and urllib3 as "Max retries exceeded … (Caused by SSLError(…))".
Python ssl module (OpenSSL) — ssl.SSLCertVerificationError, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)
What it means
Python's ssl module asked OpenSSL to verify the server's certificate, and OpenSSL could not find the issuer of the last certificate in the chain among the CA certificates this SSLContext loaded. The server is not necessarily misconfigured: often this Python simply looks at a different (or empty) CA bundle than your browser or operating system does.
Why it happens
Python from python.org on macOS without its certificates
commonThe python.org macOS installer ships its own OpenSSL, which does not read the macOS Keychain. Until "Install Certificates.command" is run for that Python version, it has no CA certificates at all, so every HTTPS request fails this way.
A private CA or a TLS-inspecting proxy
commonCompany services and corporate proxies use certificates whose root is distributed to managed machines through the OS store. Python's OpenSSL does not consult the Windows or macOS store, and Linux containers do not have the root unless it is added.
The server does not send its intermediate certificate
commonOpenSSL does not fetch missing intermediates. If the server sends only its leaf, Python fails while browsers, which cache and download intermediates, appear fine.
The CA was added to the system store, but the library uses certifi
occasionalrequests, httpx, aiohttp, botocore and pip (with legacy certificates) verify against the certifi bundle shipped as a Python package. A root installed with update-ca-certificates or in the OS keychain is invisible to them.
SSL_CERT_FILE or SSL_CERT_DIR points at the wrong place
rareThese variables replace OpenSSL's default locations. A path left over from another tool, or a bundle containing only a private root, makes public sites fail (or the reverse).
How to fix it
- 1.
macOS python.org builds: run Install Certificates.command
The script installs certifi and links it as the bundle of that Python's OpenSSL. It must be run again after installing another Python version.
bash # macOS, python.org installer: run once per installed Python version "/Applications/Python 3.14/Install Certificates.command" - 2.
Use the operating system's trust store with truststore
truststore makes ssl.SSLContext verify through the OS (Keychain on macOS, the Windows certificate store, the system bundle on Linux), so roots deployed by your IT department work in Python too. It also covers urllib3, requests and pip, which uses it by default since pip 24.2.
python # pip install truststore==0.10.5 — verify with the OS trust store (Keychain, Windows store, Linux bundle) import truststore truststore.inject_into_ssl() # call once at start-up, before creating connections import urllib.request urllib.request.urlopen("https://api.example.com/") - 3.
Load the CA explicitly for one client
Pass cafile (or cadata) to ssl.create_default_context() for code that talks to a private service. The context still verifies hostnames and dates.
python import ssl, urllib.request ctx = ssl.create_default_context(cafile="/etc/ssl/private-ca/corp-root-ca.pem") urllib.request.urlopen("https://api.example.com/", context=ctx) - 4.
Check and set the default bundle path
ssl.get_default_verify_paths() shows which file and directory OpenSSL uses. SSL_CERT_FILE and SSL_CERT_DIR override them for the process; point them at a bundle that contains both public roots and your CA.
bash # Where does this Python look by default? python3 -c "import ssl; print(ssl.get_default_verify_paths())" # Point OpenSSL-based clients at a bundle that includes your CA export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt - 5.
Server owners: send the full chain
If the handshake contains only one certificate and its issuer is a public intermediate, configure the server with the full-chain file.
bash # Server side: 1 certificate in the handshake = intermediate missing openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | grep -c 'BEGIN CERTIFICATE'
Related errors
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unab…Python urllib.requestMax retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1…requests / urllib3 (Python)There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FA…pip[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certifi…Python ssl module (OpenSSL)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)