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
- Wrap the value: ClientSession(timeout=ClientTimeout(total=30)).
- For granular control: ClientTimeout(connect=5, sock_read=30).
- 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
- Never pass a bare number as timeout; always wrap in ClientTimeout.
- Keep a single helper that converts config values to ClientTimeout.
- Annotate public APIs with ClientTimeout, not float.
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
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- ssl should be SSLContext, Fingerprint, or bool, got
- base_url must have a trailing '/'
- Connection timeout to host
- Inheritance class from ClientSession is forbidden
- Session and connector have to use same event loop
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 = loopView on GitHub (pinned to d041d4d0fd)