Python requests "certificate verify failed: unable to get local issuer" in CI
A Python client (requests, urllib3, pip) validated the server certificate and could not find the issuer to reach a trusted root. This is the Python-side form of a missing CA: the runner's certifi/CA bundle lacks the issuing CA, typically a corporate proxy root.
What this error means
A requests call or pip fails with "SSLError: ... certificate verify failed: unable to get local issuer certificate (_ssl.c:...)".
requests.exceptions.SSLError: HTTPSConnectionPool(host='api.example.com', port=443):
Max retries exceeded ... [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed:
unable to get local issuer certificate (_ssl.c:1006)Common causes
The Python CA bundle lacks the issuing CA
requests uses certifi by default; a corporate proxy or internal CA that signed the certificate is not in that bundle.
The server did not send its intermediate
If the server omits the intermediate certificate, the client cannot bridge to the root without that CA locally.
How to fix it
Point Python at the corporate CA bundle
- Obtain the corporate CA bundle in PEM format.
- Set
REQUESTS_CA_BUNDLE(andSSL_CERT_FILE) to its path. - Re-run; requests now validates against the corporate root.
env:
REQUESTS_CA_BUNDLE: /etc/ssl/certs/corp-ca-bundle.pem
SSL_CERT_FILE: /etc/ssl/certs/corp-ca-bundle.pemAppend the CA to the system store
Install the CA so both the system and Python (via SSL_CERT_FILE) trust it, avoiding per-call overrides.
cp corp-root-ca.crt /usr/local/share/ca-certificates/
update-ca-certificatesHow to prevent it
- Ship the corporate CA in runner images and export
REQUESTS_CA_BUNDLE. - Avoid
verify=False; it disables verification for the call. - Keep one CA bundle path shared by Python, curl, and git.