aio-libs/aiohttp · error · ClientConnectorCertificateError
Cannot connect to host
Error message
Cannot connect to host {host}:{port} ssl:{ssl} [{certificate_error.__class__.__name__}: {certificate_error.args}] What it means
Raised by _wrap_create_connection during a direct (non-proxy) TCP+TLS connect when the SSL handshake raises a certificate error (ssl.CertificateError or one of the cert_errors subclasses). aiohttp wraps the underlying failure in ClientConnectorCertificateError so callers can distinguish certificate problems from generic connection errors. The message echoes the original exception class and args so the CA/hostname/expiry cause is visible.
Solutions
- Build an ssl.SSLContext with load_verify_locations(cafile=...) pointing at the correct CA and pass it to the connector/session.
- Refresh the system CA bundle (install ca-certificates, run update-ca-certificates).
- Verify system clock (NTP) so cert validity windows are evaluated correctly.
- For diagnostics only, ssl=False disables verification - never use in production.
Example fix
# before
await session.get('https://internal.corp/')
# after
import ssl
ctx = ssl.create_default_context(cafile='/etc/ssl/internal-ca.pem')
connector = aiohttp.TCPConnector(ssl=ctx)
async with aiohttp.ClientSession(connector=connector) as s:
await s.get('https://internal.corp/') Defensive patterns
Strategy: try-catch
Validate before calling
import ssl
def trusted_context(cafile):
ctx = ssl.create_default_context(cafile=cafile)
return ctx
# preflight: ensure the host's cert chains to the supplied CA before trusting it
ctx = trusted_context('/etc/ssl/internal-ca.pem') Type guard
null
Try / catch
try:
resp = await session.get(url)
except aiohttp.ClientConnectorCertificateError as exc:
log.error('cert failure for %s: %s', exc.host, exc.certificate_error)
# surface to operator; do not silently disable verification
raise Prevention
- Install private/internal CAs into the trust store or SSLContext used by the connector.
- Keep the system clock synced via NTP so validity windows are correct.
- Never use ssl=False to bypass certificate errors in production code.
When it happens
Trigger: Server presents a self-signed or expired certificate; hostname on the cert does not match req.url.host; missing intermediate certificate; client's CA bundle does not trust the issuer; client clock skewed so a valid cert appears expired/not-yet-valid.
Common situations: Hitting internal services with private CAs without installing the CA; staging environments with self-signed certs; containers missing ca-certificates; system time wrong in VMs; certificate renewal lapsed.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Cannot connect to host
- ClientHttpProxyError
- Cannot initialize a TLS-in-TLS connection to host
- Connection closed
- Connection closed.
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/d5b3a707cdd226f6.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/connector.py:1347
):
sock = await aiohappyeyeballs.start_connection(
addr_infos=addr_infos,
local_addr_infos=self._local_addr_infos,
happy_eyeballs_delay=self._happy_eyeballs_delay,
interleave=self._interleave,
loop=self._loop,
socket_factory=self._socket_factory,
)
# Add ssl_shutdown_timeout for Python 3.11+ when SSL is used
if (
kwargs.get("ssl")
and self._ssl_shutdown_timeout
and sys.version_info >= (3, 11)
):
kwargs["ssl_shutdown_timeout"] = self._ssl_shutdown_timeout
return await create_connection(self._loop, *args, **kwargs, sock=sock)
except cert_errors as exc:
raise ClientConnectorCertificateError(req.connection_key, exc) from exc
except ssl_errors as exc:
raise ClientConnectorSSLError(req.connection_key, exc) from exc
except OSError as exc:
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise client_error(req.connection_key, exc) from exc
def _warn_about_tls_in_tls(
self,
underlying_transport: asyncio.Transport,
req: ClientRequest,
) -> None:
"""Issue a warning if the requested URL has HTTPS scheme."""
if req.url.scheme != "https":
return
# TLS-in-TLS only applies when the proxy itself is HTTPS.
# When the proxy is HTTP, start_tls upgrades a plain TCP connection,View on GitHub (pinned to d041d4d0fd)