{"record":{"id":"74cf838e29effcb7","repo":"pypa/pip","slug":"unable-to-verify-server-certificate-for-s","errorCode":null,"errorMessage":"Unable to verify server certificate for %s","messagePattern":"Unable to verify server certificate for (.+?)","errorType":"exception","errorClass":"CertificateError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/distlib/util.py","lineNumber":1542,"sourceCode":"            pass a connection class to do_open, but it doesn't actually check for\n            a class, and just expects a callable. As long as we behave just as a\n            constructor would have, we should be OK. If it ever changes so that\n            we *must* pass a class, we'll create an UnsafeHTTPSConnection class\n            which just sets check_domain to False in the class definition, and\n            choose which one to pass to do_open.\n            \"\"\"\n            result = HTTPSConnection(*args, **kwargs)\n            if self.ca_certs:\n                result.ca_certs = self.ca_certs\n                result.check_domain = self.check_domain\n            return result\n\n        def https_open(self, req):\n            try:\n                return self.do_open(self._conn_maker, req)\n            except URLError as e:\n                if 'certificate verify failed' in str(e.reason):\n                    raise CertificateError('Unable to verify server certificate '\n                                           'for %s' % req.host)\n                else:\n                    raise\n\n    #\n    # To prevent against mixing HTTP traffic with HTTPS (examples: A Man-In-The-\n    # Middle proxy using HTTP listens on port 443, or an index mistakenly serves\n    # HTML containing a http://xyz link when it should be https://xyz),\n    # you can use the following handler class, which does not allow HTTP traffic.\n    #\n    # It works by inheriting from HTTPHandler - so build_opener won't add a\n    # handler for HTTP itself.\n    #\n    class HTTPSOnlyHandler(HTTPSHandler, HTTPHandler):\n\n        def http_open(self, req):\n            raise URLError('Unexpected HTTP request on what should be a secure '\n                           'connection: %s' % req)","sourceCodeStart":1524,"sourceCodeEnd":1560,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/distlib/util.py#L1524-L1560","documentation":"Raised by distlib's HTTPSHandler.https_open when an HTTPS request fails with a 'certificate verify failed' URLError. The handler re-wraps the underlying ssl error into a CertificateError naming the offending host, so the caller knows exactly which server's certificate chain could not be validated against the configured CA bundle (ca_certs). This guards against MITM and stale/expired server certificates during pip's package downloads.","triggerScenarios":"Calling urlopen/https_open against a host whose TLS certificate is expired, self-signed, signed by an untrusted CA, has a hostname mismatch, or whose chain is incomplete — while ca_certs is configured on the HTTPSHandler. Also triggered when the system CA bundle is empty or missing.","commonSituations":"Corporate proxy with its own root CA not installed in the trust store; air-gapped environment with no CA bundle; pip pointed at an internal index with a self-signed cert; expired Let's Encrypt cert on a private PyPI mirror; container images that ship without ca-certificates.","solutions":["Install or update the system CA bundle (e.g. apt-get install ca-certificates, update-ca-certificates) so the server's root CA is trusted.","If the server uses a private/internal CA, point ca_certs to a PEM file containing that root certificate.","Verify the server's certificate is not expired and that the hostname matches the SAN field using openssl s_client -connect host:443.","If this is a known-internal mirror, configure pip to trust it via --trusted-host after confirming it is safe, understanding this disables verification.","Renew or re-issue the server certificate if it is expired or has an incomplete chain."],"exampleFix":"// before — no CA bundle, verification fails\nhandler = HTTPSHandler(ca_certs=None)\n\n// after — supply the correct CA bundle\nhandler = HTTPSHandler(ca_certs='/etc/ssl/certs/ca-certificates.crt', check_domain=True)","handlingStrategy":"validation","validationCode":"import ssl, socket\n\ndef verify_cert_chain(host, port=443, ca_certs=None):\n    ctx = ssl.create_default_context(cafile=ca_certs)\n    with socket.create_connection((host, port)) as sock:\n        with ctx.wrap_socket(sock, server_hostname=host) as ssock:\n            cert = ssock.getpeercert()\n    return cert is not None","typeGuard":null,"tryCatchPattern":"from ssl import CertificateError\ntry:\n    opener.open('https://example.com/')\nexcept CertificateError as e:\n    log.error('Cert verification failed for %s — check CA bundle', e)\n    raise","preventionTips":["Keep the system CA bundle updated (ca-certificates package).","Pre-validate custom CA files exist and are non-empty before passing to HTTPSHandler.","In CI, install corporate root CAs into the trust store.","Monitor certificate expiry on internal mirrors."],"tags":["ssl","certificates","network","security","tls"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}