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

  1. Confirm scheme/port: HTTPS on 443, and that the endpoint actually speaks TLS.
  2. If TLS version mismatch is confirmed, configure an ssl.SSLContext that enables the required version (temporary, scoped).
  3. Check for TLS-intercepting middleboxes and add their CA if intentional.
  4. 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

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

Related errors


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)