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 ClientSession.__init__ when the ssl argument is not an SSLContext, Fingerprint, or bool (the union SSL_ALLOWED_TYPES). aiohttp needs a concrete TLS configuration object; arbitrary values cannot be coerced. Unlike requests, a file path string is not accepted.
Solutions
- Pass a bool for default verification: ClientSession(ssl=True) (or False to disable verification).
- Load an SSLContext: ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile='ca.pem'); ClientSession(ssl=ctx).
- Pass a Fingerprint for certificate pinning: Fingerprint(hashlib.sha256(der).digest()).
Example fix
// before session = ClientSession(ssl='cert.pem') // after import ssl as _ssl ctx = _ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile='cert.pem') session = ClientSession(ssl=ctx)
Defensive patterns
Strategy: type-guard
Validate before calling
import ssl as _ssl
from aiohttp import TCPConnector
from aiohttp.typedefs import PathLike # if available
SSL_ALLOWED = (bool, _ssl.SSLContext)
# Note: Fingerprint is also allowed; import from aiohttp
def to_ssl_arg(value):
if isinstance(value, bool) or isinstance(value, _ssl.SSLContext):
return value
raise TypeError('ssl must be bool or SSLContext') Type guard
import ssl as _ssl
from aiohttp import Fingerprint
def is_valid_ssl(v) -> bool:
return isinstance(v, (bool, _ssl.SSLContext, Fingerprint)) Try / catch
try:
session = ClientSession(ssl=value)
except TypeError as e:
if 'ssl should be' in str(e):
raise SystemExit('Provide an SSLContext, Fingerprint, or bool for ssl')
raise Prevention
- Always build an ssl.SSLContext from cafile paths rather than passing paths.
- Keep TLS configuration in a single helper that returns one of the three allowed types.
- Type-annotate ssl params as 'SSLContext | Fingerprint | bool' so static checkers catch misuse.
When it happens
Trigger: Calling ClientSession(ssl='cert.pem'), ClientSession(ssl='TLSv1'), or passing a dict, int, or None where None is not the default sentinel. The isinstance(ssl, SSL_ALLOWED_TYPES) check fails.
Common situations: Migrating from requests where verify='cert.pem' / cert='path' strings are valid. Storing ssl config in a YAML/dict and passing the dict directly. Confusing the ssl flag with a cipher or protocol name string.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- fingerprint has invalid length
- md5 and sha1 are insecure and not supported. Use sha256.
- timeout parameter cannot be of
- Bad HTTP method in status line
- base_url must have a trailing '/'
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/fc39e8898db05ba7.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client.py:340
fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8",
middlewares: Sequence[ClientMiddlewareType] = (),
ssl_shutdown_timeout: _SENTINEL | None | float = sentinel,
) -> None:
# We initialise _connector to None immediately, as it's referenced in __del__()
# and could cause issues if an exception occurs during initialisation.
self._connector: BaseConnector | None = None
if base_url is None or isinstance(base_url, URL):
self._base_url: URL | None = base_url
self._base_url_origin = None if base_url is None else base_url.origin()
else:
self._base_url = URL(base_url)
self._base_url_origin = self._base_url.origin()
assert self._base_url.absolute, "Only absolute URLs are supported"
if self._base_url is not None and not self._base_url.path.endswith("/"):
raise ValueError("base_url must have a trailing '/'")
if not isinstance(ssl, SSL_ALLOWED_TYPES):
raise TypeError(
"ssl should be SSLContext, Fingerprint, or bool, "
f"got {ssl!r} instead."
)
loop = asyncio.get_running_loop()
if timeout is sentinel or timeout is None:
timeout = ClientTimeout()
if not isinstance(timeout, ClientTimeout):
raise ValueError(
f"timeout parameter cannot be of {type(timeout)} type, "
"please use 'timeout=ClientTimeout(...)'",
)
self._timeout = timeout
if ssl_shutdown_timeout is not sentinel:
warnings.warn(
"The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0",View on GitHub (pinned to d041d4d0fd)