{"id":"1ae1b10596d34584","repo":"aio-libs/aiohttp","slug":"keepalive-timeout-cannot-be-set-if-force-close-is","errorCode":null,"errorMessage":"keepalive_timeout cannot be set if force_close is True","messagePattern":"keepalive_timeout cannot be set if force_close is True","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/connector.py","lineNumber":339,"sourceCode":"\n    # abort transport after 2 seconds (cleanup broken connections)\n    _cleanup_closed_period = 2.0\n\n    allowed_protocol_schema_set = HIGH_LEVEL_SCHEMA_SET\n\n    def __init__(\n        self,\n        *,\n        keepalive_timeout: _SENTINEL | None | float = sentinel,\n        force_close: bool = False,\n        limit: int = 100,\n        limit_per_host: int = 0,\n        enable_cleanup_closed: bool = False,\n        timeout_ceil_threshold: float = 5,\n    ) -> None:\n        if force_close:\n            if keepalive_timeout is not None and keepalive_timeout is not sentinel:\n                raise ValueError(\n                    \"keepalive_timeout cannot be set if force_close is True\"\n                )\n        else:\n            if keepalive_timeout is sentinel:\n                keepalive_timeout = 15.0\n\n        self._timeout_ceil_threshold = timeout_ceil_threshold\n\n        loop = asyncio.get_running_loop()\n\n        self._closed = False\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n        # Connection pool of reusable connections.\n        # We use a deque to store connections because it has O(1) popleft()\n        # and O(1) append() operations to implement a FIFO queue.\n        self._conns: defaultdict[","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/connector.py#L321-L357","documentation":"Raised by BaseConnector.__init__ when the caller sets both `force_close=True` (no connection reuse) and an explicit `keepalive_timeout`. The two are contradictory: keepalive_timeout only matters when connections are pooled, but force_close disables pooling entirely, so aiohttp refuses the silent no-op. Passing `sentinel` or `None` is allowed because those mean 'unset'.","triggerScenarios":"Constructing a connector like `TCPConnector(force_close=True, keepalive_timeout=30)` (or via `ClientSession(connector=...)`).","commonSituations":"Copy-pasting a config block that set keepalive_timeout then flipping force_close on. Tuning for a flaky server by adding force_close without removing the timeout. Inheriting connector kwargs from a shared helper that always sets both.","solutions":["Drop the keepalive_timeout argument when force_close=True.","Or set force_close=False (the default) and keep the keepalive_timeout.","Audit the connector factory / fixtures that build connectors for tests."],"exampleFix":"# before\nconnector = aiohttp.TCPConnector(force_close=True, keepalive_timeout=30)\n# after\nconnector = aiohttp.TCPConnector(force_close=True)","handlingStrategy":"validation","validationCode":"def build_connector(force_close: bool, keepalive_timeout=None):\n    if force_close and keepalive_timeout is not None:\n        raise ValueError('do not set keepalive_timeout with force_close=True')\n    return aiohttp.TCPConnector(force_close=force_close, keepalive_timeout=keepalive_timeout)","typeGuard":null,"tryCatchPattern":"try:\n    connector = aiohttp.TCPConnector(force_close=True, keepalive_timeout=30)\nexcept ValueError:\n    connector = aiohttp.TCPConnector(force_close=True)","preventionTips":["Make force_close and keepalive_timeout mutually exclusive in your connector factory.","Centralize connector construction in one helper so config drift is caught in one place.","Add a unit test that asserts valid combinations build successfully."],"tags":["connector","config","keepalive","validation"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}