aio-libs/aiohttp · error · TypeError
ssl should be SSLContext, Fingerprint, or bool, got {ssl!r}
Error message
ssl should be SSLContext, Fingerprint, or bool, got {ssl!r} instead. What it means
Raised by TCPConnector.__init__ when the `ssl` argument is not one of SSLContext, Fingerprint, or bool (the tuple SSL_ALLOWED_TYPES). aiohttp checks at construction so that an invalid ssl policy fails fast rather than producing confusing handshake errors later. Note: on builds compiled without ssl, only bool is allowed.
Source
Thrown at aiohttp/connector.py:1025
limit_per_host: int = 0,
enable_cleanup_closed: bool = False,
timeout_ceil_threshold: float = 5,
happy_eyeballs_delay: float | None = 0.25,
interleave: int | None = None,
socket_factory: SocketFactoryType | None = None,
ssl_shutdown_timeout: _SENTINEL | None | float = sentinel,
):
super().__init__(
keepalive_timeout=keepalive_timeout,
force_close=force_close,
limit=limit,
limit_per_host=limit_per_host,
enable_cleanup_closed=enable_cleanup_closed,
timeout_ceil_threshold=timeout_ceil_threshold,
)
if not isinstance(ssl, SSL_ALLOWED_TYPES):
raise TypeError(
"ssl should be SSLContext, Fingerprint, or bool, "
f"got {ssl!r} instead."
)
self._ssl = ssl
self._resolver: AbstractResolver
if resolver is None:
self._resolver = DefaultResolver()
self._resolver_owner = True
else:
self._resolver = resolver
self._resolver_owner = False
self._use_dns_cache = use_dns_cache
self._cached_hosts = _DNSCacheTable(
ttl=ttl_dns_cache, max_size=dns_cache_max_size
)
self._throttle_dns_futures: dict[tuple[str, int], set[asyncio.Future[None]]] = (View on GitHub (pinned to c0ef574e29)
Solutions
- Pass a bool: `TCPConnector(ssl=False)` to disable verification, `ssl=True` to use a verified default context.
- Pass an `ssl.SSLContext` you configured, or an `aiohttp.Fingerprint` for pinning.
- Coerce env-var strings: `ssl=(os.environ['VERIFY'] == 'true')` rather than passing the string.
Example fix
# before connector = aiohttp.TCPConnector(ssl='false') # after connector = aiohttp.TCPConnector(ssl=False)
Defensive patterns
Strategy: type-guard
Validate before calling
import ssl
from aiohttp import Fingerprint
def coerce_ssl(v):
if isinstance(v, (ssl.SSLContext, Fingerprint, bool)):
return v
if isinstance(v, int):
return bool(v)
if isinstance(v, str):
return v.lower() == 'true'
raise TypeError(f'invalid ssl value: {v!r}') Type guard
import ssl
from aiohttp import Fingerprint
def is_valid_ssl(v) -> bool:
return isinstance(v, (ssl.SSLContext, Fingerprint, bool)) Try / catch
try:
connector = aiohttp.TCPConnector(ssl=ssl_value)
except TypeError as e:
if 'ssl should be' in str(e):
connector = aiohttp.TCPConnector(ssl=coerce_ssl(ssl_value))
raise Prevention
- Always coerce env-var-sourced ssl to bool at the config boundary.
- Keep an SSLContext (or True/False) as the canonical representation in your config dataclass.
- Unit-test the connector factory with all expected ssl input shapes.
When it happens
Trigger: Passing `TCPConnector(ssl='true')`, `ssl=1` (int, not bool), `ssl=None` to TCPConnector (None is not in the allowed tuple), or `ssl={'verify': False}`.
Common situations: String 'true'/'false' from an env var passed verbatim. Integer 0/1 instead of bool. Confusing `verify_ssl`/`fingerprint` kwargs (which go on the request) with the connector-level `ssl`. Passing None expecting 'use defaults' (use True or an SSLContext instead).
Related errors
- keepalive_timeout cannot be set if force_close is True
- Cannot connect to host {host}:{port} ssl:{ssl} [{ClassName}:
- Cannot connect to host {host}:{port} ssl:{ssl} [{strerror}]
- Cannot initialize a TLS-in-TLS connection to host {req.url.h
- ssl should be SSLContext, Fingerprint, or bool, got {ssl!r}
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/d718623b638aac29.json.
Report an issue: GitHub.