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

  1. Pass True (verify, use default context), False (disable verification - not recommended), an ssl.SSLContext, or a Fingerprint.
  2. To load a custom CA bundle, build an SSLContext and pass that, not a file path.
  3. If you need 'no ssl', omit the argument or pass the sentinel rather than None.
  4. 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

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

Related errors


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)