redis/redis-py · error · MaxConnectionsError

Too many connections

Error message

Too many connections

What it means

The non-blocking ConnectionPool raises MaxConnectionsError (a ConnectionError subclass) when get_available_connection finds the free list empty and the in-use count has reached max_connections. Unlike BlockingConnectionPool it does not wait. The most common cause is connections not being returned, but it also fires under genuine load spikes.

Solutions

  1. Increase max_connections to match your real concurrency.
  2. Ensure every acquired connection is released (use 'async with redis:' or try/finally), and look for leaks via pool metrics.
  3. Switch to BlockingConnectionPool so callers wait instead of erroring.
  4. Shorten long-running commands or move pubsub to a dedicated client/pool.

Example fix

# before
pool = ConnectionPool(host='redis.local', max_connections=10)
# after
pool = BlockingConnectionPool(host='redis.local', max_connections=50, timeout=10)
Defensive patterns

Strategy: retry

Validate before calling

if pool.get_connection_count() and all_in_use:
    # raise capacity or shed load before calling
    ...

Try / catch

from redis.exceptions import MaxConnectionsError
for attempt in range(retries):
    try:
        return await client.get(key)
    except MaxConnectionsError:
        await asyncio.sleep(backoff(attempt))
raise

Prevention

When it happens

Trigger: Every connection is in use (len(_in_use_connections) >= max_connections) and none are idle when the pool is asked for another connection; e.g. concurrency exceeds the cap, a command holds a connection for a long time, or a leak never releases.

Common situations: Long-running commands blocking connections; missing await client.aclose()/release; pubsub or monitor connections counted against the pool; load spikes with a too-small pool.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/600f05615dacc0ac. Report an issue: GitHub.

Appendix: source

Thrown at redis/asyncio/connection.py:2847

            if is_created:
                await record_connection_create_time(
                    connection_pool=self,
                    duration_seconds=time.monotonic() - start_time_created,
                )

            return connection
        except BaseException:
            await self.release(connection)
            raise

    def get_available_connection(self):
        """Get a connection from the pool, without making sure it is connected"""
        try:
            connection = self._available_connections.pop()
        except IndexError:
            if len(self._in_use_connections) >= self.max_connections:
                raise MaxConnectionsError("Too many connections") from None
            connection = self.make_connection()
        self._in_use_connections.add(connection)
        return connection

    def get_encoder(self):
        """Return an encoder based on encoding settings"""
        kwargs = self.connection_kwargs
        return self.encoder_class(
            encoding=kwargs.get("encoding", "utf-8"),
            encoding_errors=kwargs.get("encoding_errors", "strict"),
            decode_responses=kwargs.get("decode_responses", False),
        )

    def make_connection(self):
        """Create a new connection.  Can be overridden by child classes."""
        # Note: We don't record IDLE here because async uses a sync make_connection
        # but async record_connection_count. The recording is handled in get_connection.
        return self.connection_class(**self.connection_kwargs)

View on GitHub (pinned to 6a6b581b48)