aio-libs/aiohttp · error · ClientConnectorSSLError
Cannot connect to host
Error message
Cannot connect to host {host}:{port} ssl:{ssl} [{strerror}] What it means
Raised by _wrap_create_connection during a direct (non-proxy) connect when the TLS handshake or socket setup fails with an SSL error that is not a certificate error (ssl_errors branch) or with an OSError surfaced as a generic connector error. ClientConnectorSSLError covers protocol-level TLS failures (e.g. no common cipher, TLS version mismatch, handshake EOF); the OSError path produces a ClientConnectorError whose __str__ formats the strerror.
Solutions
- Confirm scheme/port: HTTPS on 443, and that the endpoint actually speaks TLS.
- If TLS version mismatch is confirmed, configure an ssl.SSLContext that enables the required version (temporary, scoped).
- Check for TLS-intercepting middleboxes and add their CA if intentional.
- Use openssl s_client -connect host:port to reproduce the handshake failure outside aiohttp.
Example fix
# before
await session.get('https://legacy.corp:443/')
# after - diagnose, then enable the missing protocol if acceptable
import ssl
ctx = ssl.create_default_context()
ctx.minimum_version = ssl.TLSVersion.TLSv1_2 # match what the server supports
async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=ctx)) as s:
await s.get('https://legacy.corp/') Defensive patterns
Strategy: try-catch
Validate before calling
import ssl
def compatible_context(minimum=ssl.TLSVersion.TLSv1_2):
ctx = ssl.create_default_context()
ctx.minimum_version = minimum
return ctx Type guard
null
Try / catch
try:
resp = await session.get(url)
except aiohttp.ClientConnectorSSLError as exc:
log.error('TLS failure for %s: %s', url, exc)
raise
except aiohttp.ClientConnectorError as exc:
log.error('connection failure for %s: %s', url, exc.os_error)
raise Prevention
- Confirm the endpoint actually speaks TLS on the target port before assuming cert issues.
- Reproduce handshake problems with openssl s_client outside the app.
- Keep the OpenSSL/SSLContext up to date so modern protocols are negotiable.
When it happens
Trigger: Server only supports TLS versions the client does not offer; cipher-suite negotiation fails; middlebox truncates the handshake; port 443 reachable but speaking plain HTTP; SNI required and not sent; firewall RST during handshake.
Common situations: Modern client hitting a legacy TLS 1.0-only server (or vice versa); corporate TLS-intercepting proxy with a broken chain; network path with aggressive RST injection; wrong scheme against a non-TLS port.
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/71487353ecf56bac.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/connector.py:1349
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,
# which is standard TLS and works on all event loops and Python versions.
if req.proxy is None or req.proxy.scheme != "https":View on GitHub (pinned to d041d4d0fd)