aio-libs/aiohttp · error · ValueError

timeout parameter cannot be of

Error message

timeout parameter cannot be of {type(timeout)} type, please use 'timeout=ClientTimeout(...)'

What it means

Raised by ClientSession.__init__ when timeout is neither None, the sentinel, nor a ClientTimeout instance. aiohttp models multiple timeouts (total, connect, sock_connect, sock_read) as a single structured object; a bare number has no unambiguous mapping.

Solutions

  1. Wrap the value: ClientSession(timeout=ClientTimeout(total=30)).
  2. For granular control: ClientTimeout(connect=5, sock_read=30).
  3. If the value comes from config as a number, convert at the boundary: ClientTimeout(total=cfg['timeout']).

Example fix

// before
session = ClientSession(timeout=30)
// after
from aiohttp import ClientTimeout
session = ClientSession(timeout=ClientTimeout(total=30))
Defensive patterns

Strategy: validation

Validate before calling

from aiohttp import ClientSession, ClientTimeout

def make_session(timeout_value):
    if isinstance(timeout_value, (int, float)):
        timeout_value = ClientTimeout(total=timeout_value)
    return ClientSession(timeout=timeout_value)

Type guard

from aiohttp import ClientTimeout

def is_client_timeout(v) -> bool:
    return isinstance(v, ClientTimeout)

Try / catch

try:
    session = ClientSession(timeout=t)
except ValueError as e:
    if 'timeout parameter cannot be' in str(e) and isinstance(t, (int, float)):
        session = ClientSession(timeout=ClientTimeout(total=t))
    else:
        raise

Prevention

When it happens

Trigger: Calling ClientSession(timeout=30), ClientSession(timeout=10.0), or passing a (connect, read) tuple. Anything that is not a ClientTimeout trips the isinstance check.

Common situations: Coming from requests/httpx where timeout=30 (seconds) is the documented form. Reading a numeric timeout from config and passing it directly. Treating timeout as a float out of habit from asyncio.wait_for.

Understand the failure class

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/92a6113ea023e31c. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/client.py:350

        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",
                DeprecationWarning,
                stacklevel=2,
            )

        if connector is None:
            connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout)
        # Initialize these three attrs before raising any exception,
        # they are used in __del__
        self._connector = connector
        self._loop = loop

View on GitHub (pinned to d041d4d0fd)