redis/redis-py · error · TemporaryUnavailableException

No database connections currently available. This is a…

Error message

No database connections currently available. This is a temporary condition - please retry the operation.

What it means

Raised as TemporaryUnavailableException by DefaultFailoverStrategyExecutor.execute() (failover.py:114-121) when WeightBasedFailoverStrategy.database() raised NoValidDatabaseException but the failover retry budget (failover_attempts) has not yet been exhausted. It is the transient sibling of 154: the system believes databases may recover shortly and tells the caller to retry rather than failing hard.

Solutions

  1. Retry the operation with backoff — the message explicitly says this is temporary. Combine with command_retry tuning.
  2. Increase failover_attempts and/or failover_delay in MultiDbConfig to give circuits more time to recover before the hard NoValidDatabaseException.
  3. Reduce circuit reset_timeout/grace_period so circuits return to HALF_OPEN/CLOSED faster.
  4. Catch TemporaryUnavailableException separately from NoValidDatabaseException and apply a bounded retry with jitter; fail open to a cache/degraded path if exhausted.
  5. Investigate why all circuits are simultaneously open (shared failure domain).

Example fix

// before
resp = await client.get("k")  # TemporaryUnavailableException bubbles up & crashes caller

// after
import asyncio
from redis.multidb.exception import TemporaryUnavailableException

for attempt in range(5):
    try:
        resp = await client.get("k"); break
    except TemporaryUnavailableException:
        await asyncio.sleep(0.2 * (2 ** attempt))
else:
    resp = await cache.get("k")  # degrade
Defensive patterns

Strategy: retry

Validate before calling

from redis.multidb.circuit import State as CBState

def likely_temporary(client) -> bool:
    # circuits are not all closed, but at least one is HALF_OPEN (recovering)
    states = {d.circuit.state for d, _ in client.get_databases()}
    return CBState.HALF_OPEN in states or CBState.OPEN in states

# if likely_temporary(client): retry with backoff instead of failing

Type guard

from redis.multidb.circuit import State as CBState

def has_recovering_circuit(client) -> bool:
    return any(d.circuit.state == CBState.HALF_OPEN for d, _ in client.get_databases())

Try / catch

import asyncio
from redis.multidb.exception import TemporaryUnavailableException, NoValidDatabaseException

async def call_with_retry(client, fn, *args, attempts=5, base=0.2):
    for i in range(attempts):
        try:
            return await fn(client, *args)
        except TemporaryUnavailableException:
            await asyncio.sleep(base * (2 ** i))
    # final attempt: let NoValidDatabaseException surface if circuits never recover
    return await fn(client, *args)

Prevention

When it happens

Trigger: Any command whose execution triggers _check_active_database() during a window where all circuits are OPEN/HALF_OPEN and the failover_counter is still <= failover_attempts (default 10, spaced by failover_delay default 12s). The exception propagates out of execute_command/execute_pipeline/execute_transaction.

Common situations: Brief total outages during a failover storm, all databases momentarily in HALF_OPEN awaiting their next probe, a rolling restart of every region, or network blips lasting on the order of failover_delay × failover_attempts.

Related errors


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

Appendix: source

Thrown at redis/asyncio/multidb/failover.py:118

    async def execute(self) -> AsyncDatabase:
        try:
            database = await self._strategy.database()
            self._reset()
            return database
        except NoValidDatabaseException as e:
            if self._next_attempt_ts == 0:
                self._next_attempt_ts = time.time() + self._failover_delay
                self._failover_counter += 1
            elif time.time() >= self._next_attempt_ts:
                self._next_attempt_ts += self._failover_delay
                self._failover_counter += 1

            if self._failover_counter > self._failover_attempts:
                self._reset()
                raise e
            else:
                raise TemporaryUnavailableException(
                    "No database connections currently available. "
                    "This is a temporary condition - please retry the operation."
                )

    def _reset(self) -> None:
        self._next_attempt_ts = 0
        self._failover_counter = 0

View on GitHub (pinned to 6a6b581b48)