aio-libs/aiohttp · error · ClientConnectionError

Cannot initialize a TLS-in-TLS connection to host {req.url.h

Error message

Cannot initialize a TLS-in-TLS connection to host {req.url.host!s}:{req.url.port:d} through an underlying connection to an HTTPS proxy {req.proxy!s} ssl:{req.ssl or 'default'} [{type_err!s}]

What it means

Raised inside _start_tls_connection() when `start_tls()` raises a TypeError because the underlying asyncio transport does not support being upgraded (the classic TLS-in-TLS limitation). aiohttp's `_warn_about_tls_in_tls` only suppresses the warning for uvloop, aiofastnet, or Python >=3.11; on older asyncio stdlib doing HTTPS-target-through-HTTPS-proxy, start_tls() rejects the transport and aiohttp wraps the TypeError as ClientConnectionError with a descriptive message.

Source

Thrown at aiohttp/connector.py:1480

                        except ServerFingerprintMismatch:
                            tls_transport.close()
                            if not self._cleanup_closed_disabled:
                                self._cleanup_closed_transports.append(tls_transport)
                            raise
        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
        except TypeError as type_err:
            # Example cause looks like this:
            # TypeError: transport <asyncio.sslproto._SSLProtocolTransport
            # object at 0x7f760615e460> is not supported by start_tls()

            raise ClientConnectionError(
                "Cannot initialize a TLS-in-TLS connection to host "
                f"{req.url.host!s}:{req.url.port:d} through an underlying connection "
                f"to an HTTPS proxy {req.proxy!s} ssl:{req.ssl or 'default'} "
                f"[{type_err!s}]"
            ) from type_err
        else:
            if tls_transport is None:
                msg = "Failed to start TLS (possibly caused by closing transport)"
                raise client_error(req.connection_key, OSError(msg))
            tls_proto.connection_made(
                tls_transport
            )  # Kick the state machine of the new TLS protocol

        return tls_transport, tls_proto

    def _convert_hosts_to_addr_infos(
        self, hosts: list[ResolveResult]
    ) -> list[AddrInfoType]:

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Upgrade to Python 3.11+ where stdlib asyncio supports TLS-in-TLS (bpo-44011).
  2. Switch the event loop to uvloop (or aiofastnet) which supports TLS-in-TLS on older Python.
  3. Use an HTTP (`http://`) proxy instead of an HTTPS proxy where possible - that path uses plain TCP then TLS, no nested TLS.
  4. Apply the documented monkeypatch (see aiohttp proxy docs / discussion #6044) only as a last resort.

Example fix

# before (Python 3.10, default asyncio)
async with session.get('https://target', proxy='https://proxy:443') as r: ...
# after
import uvloop
uvloop.install()  # before creating the event loop
# or upgrade to Python 3.11+
Defensive patterns

Strategy: validation

Validate before calling

import sys

def supports_tls_in_tls() -> bool:
    return sys.version_info >= (3, 11)

Try / catch

from aiohttp import ClientConnectionError
try:
    await session.get('https://target', proxy='https://proxy:443')
except ClientConnectionError as e:
    if 'TLS-in-TLS' in str(e):
        # switch to uvloop, upgrade Python, or use an http:// proxy
        ...
    raise

Prevention

When it happens

Trigger: Sending an HTTPS request through an HTTPS (`https://...`) proxy on Python <3.11 using the default asyncio event loop (not uvloop, not aiofastnet). The plain-HTTP-proxy path is unaffected.

Common situations: Corporate HTTPS proxies (`https://proxy:443`) on legacy Python. CI pinned to Python 3.10. Switching from uvloop to default asyncio on an app that used HTTPS proxies.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/733b0597730e40df.json. Report an issue: GitHub.