{"id":"600f05615dacc0ac","repo":"redis/redis-py","slug":"too-many-connections","errorCode":null,"errorMessage":"Too many connections","messagePattern":"Too many connections","errorType":"exception","errorClass":"MaxConnectionsError","httpStatus":null,"severity":"error","filePath":"redis/asyncio/connection.py","lineNumber":2844,"sourceCode":"\n            if is_created:\n                await record_connection_create_time(\n                    connection_pool=self,\n                    duration_seconds=time.monotonic() - start_time_created,\n                )\n\n            return connection\n        except BaseException:\n            await self.release(connection)\n            raise\n\n    def get_available_connection(self):\n        \"\"\"Get a connection from the pool, without making sure it is connected\"\"\"\n        try:\n            connection = self._available_connections.pop()\n        except IndexError:\n            if len(self._in_use_connections) >= self.max_connections:\n                raise MaxConnectionsError(\"Too many connections\") from None\n            connection = self.make_connection()\n        self._in_use_connections.add(connection)\n        return connection\n\n    def get_encoder(self):\n        \"\"\"Return an encoder based on encoding settings\"\"\"\n        kwargs = self.connection_kwargs\n        return self.encoder_class(\n            encoding=kwargs.get(\"encoding\", \"utf-8\"),\n            encoding_errors=kwargs.get(\"encoding_errors\", \"strict\"),\n            decode_responses=kwargs.get(\"decode_responses\", False),\n        )\n\n    def make_connection(self):\n        \"\"\"Create a new connection.  Can be overridden by child classes.\"\"\"\n        # Note: We don't record IDLE here because async uses a sync make_connection\n        # but async record_connection_count. The recording is handled in get_connection.\n        return self.connection_class(**self.connection_kwargs)","sourceCodeStart":2826,"sourceCodeEnd":2862,"githubUrl":"https://github.com/redis/redis-py/blob/da03cdc7e8731092b13e395605c3c1fb2de25de1/redis/asyncio/connection.py#L2826-L2862","documentation":"Raised as MaxConnectionsError (a subclass of ConnectionError) by get_available_connection() when no idle connection is available and the number of in-use connections has reached max_connections. The pool is exhausted and cannot create a new connection. This is the non-blocking pool's hard limit; BlockingConnectionPool waits instead (see [130]).","triggerScenarios":"Using the default (non-blocking) ConnectionPool and issuing more concurrent commands than max_connections without releasing connections first. Happens with pipelines/transactions held open, leaked connections (not released), or genuine concurrency above the limit. Fires at connection.py:2843.","commonSituations":"Leaking connections (e.g., a command that errors before release); long-running pipelines/transactions holding many connections; undersized max_connections for the workload; async tasks fanning out faster than connections are returned.","solutions":["Increase max_connections to match your real concurrency.","Ensure every borrowed connection is released (use 'async with redis:' context or release in finally).","Audit for leaked connections and long-held pipelines/transactions.","Switch to BlockingConnectionPool if you want callers to wait instead of erroring."],"exampleFix":"// before\nfor i in range(500):\n    asyncio.create_task(redis.get(f'k{i}'))  # exceeds max_connections=100\n// after\nsem = asyncio.Semaphore(redis.connection_pool.max_connections)\nasync def guarded(i):\n    async with sem:\n        await redis.get(f'k{i}')\nawait asyncio.gather(*(guarded(i) for i in range(500)))","handlingStrategy":"retry","validationCode":"def safe_concurrency_limit(pool, tasks):\n    # avoid fanning out beyond the pool\n    return min(len(tasks), pool.max_connections)\n\nlimit = safe_concurrency_limit(redis.connection_pool, tasks)\nsem = asyncio.Semaphore(limit)","typeGuard":"def pool_saturated(pool) -> bool:\n    return len(pool._in_use_connections) >= pool.max_connections","tryCatchPattern":"from redis.exceptions import MaxConnectionsError\ntry:\n    await redis.get('k')\nexcept MaxConnectionsError:\n    await asyncio.sleep(backoff)\n    await redis.get('k')  # retry after a connection is freed","preventionTips":["Set max_connections to match real concurrency.","Always release connections (async with redis: ...).","Bound fan-out with a Semaphore sized to max_connections.","Switch to BlockingConnectionPool to wait instead of error."],"tags":["pool","connection-limit","concurrency","asyncio"],"analyzedSha":"da03cdc7e8731092b13e395605c3c1fb2de25de1","analyzedAt":"2026-08-04T20:26:47.563Z","schemaVersion":2}