Error message
<urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
Also printed as
- urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate
The text inside the brackets is the underlying ssl error and varies ("self-signed certificate in certificate chain", "certificate has expired", …). Python closes it with a source location and ">", e.g. "(_ssl.c:1082)>".
Python urllib.request — urllib.error.URLError wrapping ssl.SSLCertVerificationError
What it means
urllib.request.urlopen() wraps any connection problem in URLError, so the TLS failure appears inside "<urlopen error …>". When this happens for every public site, including www.python.org, the Python process has no usable CA certificates at all: the classic case is the python.org installer on macOS, whose bundled OpenSSL does not read the Keychain until "Install Certificates.command" has been run.
Why it happens
python.org Python on macOS without its certificates
commonThe installer ships OpenSSL with an empty trust store and a helper script, "Install Certificates.command", which installs certifi and links it. Skipping it (or installing a new minor version later) leaves urlopen unable to verify anything.
A minimal image or custom Python build without a CA bundle
occasionalPython compiled from source or copied into a scratch or distroless image looks for the OpenSSL default bundle path, which does not exist there.
A TLS-inspecting proxy
occasionalIf only some sites fail, or failures started on a company network, a proxy root that this Python does not trust is the likely cause.
SSL_CERT_FILE or SSL_CERT_DIR points to the wrong place
rareThese variables replace the default locations for the whole process; an empty or unrelated file breaks every verification.
How to fix it
- 1.
macOS: run Install Certificates.command
Run it from Finder or the terminal for each python.org Python version, then confirm the default CA file path exists.
bash # macOS, python.org installer (one script per installed version) "/Applications/Python 3.14/Install Certificates.command" python3 -c "import ssl; print(ssl.get_default_verify_paths().openssl_cafile)" - 2.
Pass a context built from certifi
For scripts that must work everywhere, build the SSL context from certifi.where() explicitly; keep certifi updated so its roots stay current.
python # Explicit, portable: use certifi's Mozilla bundle for this request # pip install certifi import certifi, ssl, urllib.request ctx = ssl.create_default_context(cafile=certifi.where()) urllib.request.urlopen("https://www.python.org/", context=ctx) - 3.
Or use the operating system's trust store
truststore.inject_into_ssl() makes urllib, http.client and libraries built on ssl verify through the OS, including company roots deployed by IT.
python # Or verify with the operating system's store (macOS Keychain, Windows, Linux bundle) # pip install truststore==0.10.5 import truststore truststore.inject_into_ssl() - 4.
Check for environment overrides
Unset SSL_CERT_FILE and SSL_CERT_DIR if they point to files that do not contain the needed roots.
bash env | grep -E '^SSL_CERT_(FILE|DIR)=' python3 -c "import ssl; print(ssl.get_default_verify_paths())"