pypa/pip · error · CertificateError

Unable to verify server certificate for %s

Error message

Unable to verify server certificate for %s

What it means

Raised by HTTPSHandler.https_open() when an HTTPS request fails with a URLError whose reason contains 'certificate verify failed'. distlib re-wraps this as a ssl.CertificateError (subclass of ValueError) named 'Unable to verify server certificate for <host>', indicating the server's TLS certificate could not be validated against the configured CA bundle or hostname.

Source

Thrown at src/pip/_vendor/distlib/util.py:1542

            pass a connection class to do_open, but it doesn't actually check for
            a class, and just expects a callable. As long as we behave just as a
            constructor would have, we should be OK. If it ever changes so that
            we *must* pass a class, we'll create an UnsafeHTTPSConnection class
            which just sets check_domain to False in the class definition, and
            choose which one to pass to do_open.
            """
            result = HTTPSConnection(*args, **kwargs)
            if self.ca_certs:
                result.ca_certs = self.ca_certs
                result.check_domain = self.check_domain
            return result

        def https_open(self, req):
            try:
                return self.do_open(self._conn_maker, req)
            except URLError as e:
                if 'certificate verify failed' in str(e.reason):
                    raise CertificateError('Unable to verify server certificate '
                                           'for %s' % req.host)
                else:
                    raise

    #
    # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The-
    # Middle proxy using HTTP listens on port 443, or an index mistakenly serves
    # HTML containing a http://xyz link when it should be https://xyz),
    # you can use the following handler class, which does not allow HTTP traffic.
    #
    # It works by inheriting from HTTPHandler - so build_opener won't add a
    # handler for HTTP itself.
    #
    class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler):

        def http_open(self, req):
            raise URLError('Unexpected HTTP request on what should be a secure '
                           'connection: %s' % req)

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Update the system CA store (e.g. 'apt-get install --reinstall ca-certificates' / 'update-ca-certificates').
  2. Point ca_certs at the correct PEM bundle (e.g. certifi's cacert.pem or your org's bundle).
  3. Fix system clock skew (NTP) so cert validity periods are evaluated correctly.
  4. As a last resort on trusted networks, disable verification explicitly rather than letting it fail silently, and document the risk.

Example fix

// before
index = PackageIndex(..., ca_certs='/missing/bundle.pem')
// after
import certifi
index = PackageIndex(..., ca_certs=certifi.where())
Defensive patterns

Strategy: validation

Validate before calling

import os, ssl

def ca_bundle_is_usable(ca_certs):
    if not ca_certs or not os.path.exists(ca_certs):
        return False
    try:
        ctx = ssl.create_default_context(cafile=ca_certs)
        ctx.get_ca_certs()  # forces load
        return True
    except ssl.SSLError:
        return False

def safe_index(url, ca_certs):
    if not ca_bundle_is_usable(ca_certs):
        raise ValueError('CA bundle missing or invalid: %r' % ca_certs)
    # proceed to build PackageIndex/HTTPSHandler with ca_certs

Try / catch

from ssl import CertificateError
try:
    opener.open(req)
except CertificateError as e:
    if 'Unable to verify server certificate' in str(e):
        refresh_ca_bundle()  # update ca-certificates / certifi
        # then retry once; do NOT silently disable verification in production
        opener.open(req)
    else:
        raise

Prevention

When it happens

Trigger: Configuring a PackageIndex/HTTPSHandler with ca_certs pointing at a missing/expired CA bundle, connecting to an index URL with a self-signed or expired certificate, a system clock skew invalidating cert validity, or missing root CAs on the OS.

Common situations: Corporate proxies with custom CAs not in the trust store, stale pip/distlib ca_certs, air-gapped environments, expired Let's Encrypt certs, or container images without ca-certificates installed.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/74cf838e29effcb7.json. Report an issue: GitHub.