aio-libs/aiohttp · error · ValueError

total timeout must be a positive number or None to disable,

Error message

total timeout must be a positive number or None to disable, got 0. Using 0 to disable timeouts is no longer supported, use None instead.

What it means

Raised as ValueError in ClientTimeout.__post_init__ when the 'total' field equals exactly 0. Historically some users passed total=0 to mean 'disable timeout'; aiohttp now requires total=None for that, because 0 is ambiguous with 'immediately time out'. The post_init also clamps total to be >= any specific timeout, so a negative or zero total after clamping triggers this.

Source

Thrown at aiohttp/client_reqrep.py:125

    def __post_init__(self) -> None:
        # Ensure total is never lower than a more specific timeout, otherwise
        # the latter would be silently capped by total and rendered useless.
        # total=None means the user explicitly disabled the total timeout.
        if self.total is None:
            return
        object.__setattr__(
            self,
            "total",
            max(
                self.total,
                self.connect or 0,
                self.sock_read or 0,
                self.sock_connect or 0,
            ),
        )

        if self.total == 0:
            raise ValueError(
                "total timeout must be a positive number or None to disable, "
                "got 0. Using 0 to disable timeouts is no longer supported, "
                "use None instead."
            )


def _gen_default_accept_encoding() -> str:
    encodings = [
        "gzip",
        "deflate",
    ]
    if HAS_BROTLI:
        encodings.append("br")
    if HAS_ZSTD:
        encodings.append("zstd")
    return ", ".join(encodings)

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Pass total=None to disable the overall timeout.
  2. Pass a positive float (e.g. 30.0) for a real timeout ceiling.
  3. Audit dynamic config: coerce 0 to None before constructing ClientTimeout.

Example fix

# before
client_timeout = aiohttp.ClientTimeout(total=0)
# after
client_timeout = aiohttp.ClientTimeout(total=None)  # disable total timeout
Defensive patterns

Strategy: validation

Validate before calling

def make_timeout(total):
    if total == 0:
        total = None  # treat 0 as disable
    return aiohttp.ClientTimeout(total=total)

Type guard

def is_valid_total(total) -> bool:
    return total is None or (isinstance(total, (int, float)) and total > 0)

Prevention

When it happens

Trigger: Fires at line 124-129 when self.total is not None and resolves to 0 after max() with connect/sock_read/sock_connect. Common when constructing ClientTimeout(total=0) or when all specific timeouts are 0/None and total is 0.

Common situations: Migrating from older aiohttp or requests where timeout=0 meant disable; copy-pasted configs; building ClientTimeout from dynamic config where a 0 slips in.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/1e34f2e7566d9b59.json. Report an issue: GitHub.