aio-libs/aiohttp · error · TypeError
ssl should be SSLContext, Fingerprint, or bool, got
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 in SSL_ALLOWED_TYPES (SSLContext, Fingerprint, or bool). The ssl parameter controls TLS behavior for every connection the connector makes, so an unexpected type would propagate ambiguously; aiohttp rejects it immediately with the offending value echoed via %r.
Solutions
- Pass True (verify, use default context), False (disable verification - not recommended), an ssl.SSLContext, or a Fingerprint.
- To load a custom CA bundle, build an SSLContext and pass that, not a file path.
- If you need 'no ssl', omit the argument or pass the sentinel rather than None.
- Type-check config-derived ssl values before constructing the connector.
Example fix
# before connector = aiohttp.TCPConnector(ssl='/etc/ssl/certs/ca.pem') # after import ssl ctx = ssl.create_default_context(cafile='/etc/ssl/certs/ca.pem') connector = aiohttp.TCPConnector(ssl=ctx)
Defensive patterns
Strategy: type-guard
Validate before calling
import ssl
from aiohttp import Fingerprint
def valid_ssl(value) -> bool:
return isinstance(value, (ssl.SSLContext, Fingerprint, bool))
assert valid_ssl(configured_ssl), 'ssl must be SSLContext, Fingerprint, or bool' Type guard
import ssl
from aiohttp import Fingerprint
from typing import Union
def is_ssl_allowed(value) -> bool:
return isinstance(value, (ssl.SSLContext, Fingerprint, bool)) Try / catch
try:
connector = aiohttp.TCPConnector(ssl=configured_ssl)
except TypeError as exc:
if 'ssl should be' in str(exc):
connector = aiohttp.TCPConnector(ssl=True) # safe default
else:
raise Prevention
- Build ssl.SSLContext for custom CA bundles - never pass a path string as ssl.
- Type-check config-derived ssl values before constructing the connector.
- Treat None as 'use the sentinel', not a valid ssl value.
When it happens
Trigger: Passing ssl='True' (string), ssl=None (NoneType is not allowed - use the sentinel or False), ssl=an SSL enum, ssl=a pathlib path to a cert, or ssl=some_object from a misread tutorial.
Common situations: Confusing ssl with verify_ssl/cert loading helpers; passing an ssl enum from another library; copy-pasting code that targeted a different HTTP client; deserializing config from JSON where True became 'True'.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- keepalive_timeout cannot be set if force_close is True
- Can not serialize value type: %r headers: %r value: %r
- Cannot connect to host
- Cannot connect to host
- Cannot connect to unix socket
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/d718623b638aac29.
Report an issue: GitHub.
Appendix: 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 d041d4d0fd)