{"id":"1e34f2e7566d9b59","repo":"aio-libs/aiohttp","slug":"total-timeout-must-be-a-positive-number-or-none-to","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_reqrep.py","lineNumber":125,"sourceCode":"    def __post_init__(self) -> None:\n        # Ensure total is never lower than a more specific timeout, otherwise\n        # the latter would be silently capped by total and rendered useless.\n        # total=None means the user explicitly disabled the total timeout.\n        if self.total is None:\n            return\n        object.__setattr__(\n            self,\n            \"total\",\n            max(\n                self.total,\n                self.connect or 0,\n                self.sock_read or 0,\n                self.sock_connect or 0,\n            ),\n        )\n\n        if self.total == 0:\n            raise ValueError(\n                \"total timeout must be a positive number or None to disable, \"\n                \"got 0. Using 0 to disable timeouts is no longer supported, \"\n                \"use None instead.\"\n            )\n\n\ndef _gen_default_accept_encoding() -> str:\n    encodings = [\n        \"gzip\",\n        \"deflate\",\n    ]\n    if HAS_BROTLI:\n        encodings.append(\"br\")\n    if HAS_ZSTD:\n        encodings.append(\"zstd\")\n    return \", \".join(encodings)\n\n","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_reqrep.py#L107-L143","documentation":"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.","triggerScenarios":"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.","commonSituations":"Migrating from older aiohttp or requests where timeout=0 meant disable; copy-pasted configs; building ClientTimeout from dynamic config where a 0 slips in.","solutions":["Pass total=None to disable the overall timeout.","Pass a positive float (e.g. 30.0) for a real timeout ceiling.","Audit dynamic config: coerce 0 to None before constructing ClientTimeout."],"exampleFix":"# before\nclient_timeout = aiohttp.ClientTimeout(total=0)\n# after\nclient_timeout = aiohttp.ClientTimeout(total=None)  # disable total timeout","handlingStrategy":"validation","validationCode":"def make_timeout(total):\n    if total == 0:\n        total = None  # treat 0 as disable\n    return aiohttp.ClientTimeout(total=total)","typeGuard":"def is_valid_total(total) -> bool:\n    return total is None or (isinstance(total, (int, float)) and total > 0)","tryCatchPattern":null,"preventionTips":["Never pass total=0; use None to disable.","Validate dynamic config: coerce 0 -> None before constructing ClientTimeout.","Add a unit test that asserts ClientTimeout(total=0) raises."],"tags":["timeout","configuration","http-client","migration"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}