aio-libs/aiohttp · error · ClientConnectionError
Cannot initialize a TLS-in-TLS connection to host
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 from _start_tls_connection when start_tls() raises TypeError - historically because the underlying asyncio transport does not support start_tls (the stdlib asyncio SSL transport could not be re-upgraded before Python 3.11, bpo-44011). aiohttp catches the TypeError and re-raises it as ClientConnectionError with the explicit 'Cannot initialize a TLS-in-TLS connection' message so the cause is obvious. It is the hard-failure counterpart of the runtime warning emitted by _warn_about_tls_in_tls.
Solutions
- Upgrade to Python 3.11+ where asyncio natively supports TLS-in-TLS.
- Switch to uvloop (or aiofastnet if available) - their transports advertise start_tls compatibility.
- Reconfigure egress to use an HTTP (CONNECT) proxy so only one TLS layer is needed.
- Avoid chaining an HTTPS target through an HTTPS proxy if the runtime cannot satisfy TLS-in-TLS.
Example fix
# before - Python 3.10, default asyncio
await session.get('https://target/', proxy='https://corp-proxy:443')
# after - use an HTTP proxy instead
await session.get('https://target/', proxy='http://corp-proxy:8080')
# or upgrade to Python 3.11+ / install uvloop Defensive patterns
Strategy: fallback
Validate before calling
import sys
def can_tls_in_tls() -> bool:
return sys.version_info >= (3, 11)
if not can_tls_in_tls() and proxy_url.startswith('https://'):
raise RuntimeError('use an HTTP proxy or upgrade to Python 3.11+ for HTTPS-over-HTTPS') Type guard
null
Try / catch
try:
resp = await session.get(url, proxy=proxy_url)
except aiohttp.ClientConnectionError as exc:
if 'TLS-in-TLS' in str(exc):
# fallback to an HTTP proxy (single TLS layer)
http_proxy = proxy_url.replace('https://', 'http://')
resp = await session.get(url, proxy=http_proxy)
else:
raise Prevention
- Prefer an HTTP CONNECT proxy when the runtime cannot do TLS-in-TLS.
- Target Python 3.11+ or install uvloop for environments that need HTTPS-over-HTTPS proxying.
- Heed the runtime warning from _warn_about_tls_in_tls before it becomes this hard error.
When it happens
Trigger: HTTPS request through an HTTPS proxy on Python < 3.11 with the default asyncio loop; using a custom event loop whose transport lacks start_tls support; uvloop/aiofastnet absent so the stdlib limitation applies.
Common situations: Legacy Python runtime forced to do TLS-in-TLS; corporate HTTPS-proxy-only egress on an older interpreter; event loop without start_tls-compatible transports.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- aiohttp 4.x requires Python 3.10+
- aiohttp only supports http(s) proxies
- Cannot connect to host
- Cannot connect to host
- ClientHttpProxyError
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/733b0597730e40df.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)