{"record":{"id":"83ef0165fa1bed93","repo":"redis/redis-py","slug":"no-database-connections-currently-available-this","errorCode":null,"errorMessage":"No database connections currently available. This is a temporary condition - please retry the operation.","messagePattern":"No database connections currently available\\. This is a temporary condition - please retry the operation\\.","errorType":"exception","errorClass":"TemporaryUnavailableException","httpStatus":null,"severity":"error","filePath":"redis/asyncio/multidb/failover.py","lineNumber":118,"sourceCode":"\n    async def execute(self) -> AsyncDatabase:\n        try:\n            database = await self._strategy.database()\n            self._reset()\n            return database\n        except NoValidDatabaseException as e:\n            if self._next_attempt_ts == 0:\n                self._next_attempt_ts = time.time() + self._failover_delay\n                self._failover_counter += 1\n            elif time.time() >= self._next_attempt_ts:\n                self._next_attempt_ts += self._failover_delay\n                self._failover_counter += 1\n\n            if self._failover_counter > self._failover_attempts:\n                self._reset()\n                raise e\n            else:\n                raise TemporaryUnavailableException(\n                    \"No database connections currently available. \"\n                    \"This is a temporary condition - please retry the operation.\"\n                )\n\n    def _reset(self) -> None:\n        self._next_attempt_ts = 0\n        self._failover_counter = 0\n","sourceCodeStart":100,"sourceCodeEnd":126,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/asyncio/multidb/failover.py#L100-L126","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the operation with backoff — the message explicitly says this is temporary. Combine with command_retry tuning.","Increase failover_attempts and/or failover_delay in MultiDbConfig to give circuits more time to recover before the hard NoValidDatabaseException.","Reduce circuit reset_timeout/grace_period so circuits return to HALF_OPEN/CLOSED faster.","Catch TemporaryUnavailableException separately from NoValidDatabaseException and apply a bounded retry with jitter; fail open to a cache/degraded path if exhausted.","Investigate why all circuits are simultaneously open (shared failure domain)."],"exampleFix":"// before\nresp = await client.get(\"k\")  # TemporaryUnavailableException bubbles up & crashes caller\n\n// after\nimport asyncio\nfrom redis.multidb.exception import TemporaryUnavailableException\n\nfor attempt in range(5):\n    try:\n        resp = await client.get(\"k\"); break\n    except TemporaryUnavailableException:\n        await asyncio.sleep(0.2 * (2 ** attempt))\nelse:\n    resp = await cache.get(\"k\")  # degrade","handlingStrategy":"retry","validationCode":"from redis.multidb.circuit import State as CBState\n\ndef likely_temporary(client) -> bool:\n    # circuits are not all closed, but at least one is HALF_OPEN (recovering)\n    states = {d.circuit.state for d, _ in client.get_databases()}\n    return CBState.HALF_OPEN in states or CBState.OPEN in states\n\n# if likely_temporary(client): retry with backoff instead of failing","typeGuard":"from redis.multidb.circuit import State as CBState\n\ndef has_recovering_circuit(client) -> bool:\n    return any(d.circuit.state == CBState.HALF_OPEN for d, _ in client.get_databases())","tryCatchPattern":"import asyncio\nfrom redis.multidb.exception import TemporaryUnavailableException, NoValidDatabaseException\n\nasync def call_with_retry(client, fn, *args, attempts=5, base=0.2):\n    for i in range(attempts):\n        try:\n            return await fn(client, *args)\n        except TemporaryUnavailableException:\n            await asyncio.sleep(base * (2 ** i))\n    # final attempt: let NoValidDatabaseException surface if circuits never recover\n    return await fn(client, *args)","preventionTips":["Catch TemporaryUnavailableException separately from NoValidDatabaseException and retry with jittered backoff.","Tune failover_attempts/failover_delay to give circuits time to recover before the hard failure.","Monitor TemporaryUnavailableException rate as an early-warning for cascading outages.","Provide a bounded retry wrapper around command execution.","Ensure circuits have a short reset_timeout so HALF_OPEN probes happen quickly."],"tags":["multidb","failover","retry","circuit-breaker","transient","routing"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}