{"record":{"id":"3ffeef734db208d1","repo":"redis/jedis","slug":"no-healthy-database-available-after-initialization","errorCode":null,"errorMessage":"No healthy database available after initialization policy succeeded.","messagePattern":"No healthy database available after initialization policy succeeded\\.","errorType":"exception","errorClass":"JedisConnectionException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/mcf/MultiDbConnectionProvider.java","lineNumber":502,"sourceCode":"      // Check if health checks are enabled for this endpoint\n      if (healthStatusManager.hasHealthCheck(endpoint)) {\n        status = healthStatusManager.getHealthStatus(endpoint);\n      } else {\n        // No health check configured - assume healthy\n        log.info(\"No health check configured for database {}, defaulting to HEALTHY\", endpoint);\n        status = HealthStatus.HEALTHY;\n      }\n\n      if (status.isHealthy()) {\n        log.info(\"Found healthy database: {} (weight: {})\", endpoint, database.getWeight());\n        return database;\n      } else {\n        log.info(\"Database {} is unhealthy, trying next database\", endpoint);\n      }\n    }\n\n    // No healthy database found (should not happen if policy succeeded)\n    throw new JedisConnectionException(\n        \"No healthy database available after initialization policy succeeded.\");\n  }\n\n  /**\n   * Periodic failback checker - runs at configured intervals to check for failback opportunities\n   */\n  @VisibleForTesting\n  void periodicFailbackCheck() {\n    try {\n      // Find the best candidate database for failback\n      Map.Entry<Endpoint, Database> bestCandidate = null;\n      float bestWeight = activeDatabase.getWeight();\n\n      for (Map.Entry<Endpoint, Database> entry : databaseMap.entrySet()) {\n        Database database = entry.getValue();\n\n        // Skip if this is already the active database\n        if (database == activeDatabase) {","sourceCodeStart":484,"sourceCodeEnd":520,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/mcf/MultiDbConnectionProvider.java#L484-L520","documentation":"selectBestAvailableDatabase is only called after the InitializationPolicy reported SUCCESS, yet when it scans databases in weight order it expects to find at least one healthy database. If every status check comes back unhealthy anyway (statuses changed between the policy evaluation and the selection, or no health check is configured and status is still unhealthy), this defensive JedisConnectionException is thrown. Per the source comment, it 'should not happen if policy succeeded', indicating a race or inconsistent health state.","triggerScenarios":"waitForInitializationPolicy gets Decision.SUCCESS from the policy, then selectBestAvailableDatabase iterates sortedDatabases and healthStatusManager.getHealthStatus(endpoint) returns unhealthy for every endpoint — a race where health statuses flip to unhealthy between policy evaluation and database selection, or a policy that succeeds despite no healthy status (e.g. a custom policy).","commonSituations":"All databases become unhealthy in the window between policy evaluation and selection (mass outage starting exactly at startup); a custom InitializationPolicy whose SUCCESS logic does not require any healthy database; flapping health checks that pass at evaluation time and fail moments later.","solutions":["Retry client construction; if transient, a healthy state at build time avoids the race.","If using a custom InitializationPolicy, ensure its SUCCESS decision actually requires at least one healthy database.","Investigate why all endpoints turned unhealthy in that instant (check health-check logs and Redis server availability).","Tune health check thresholds so borderline/flapping endpoints are not toggling status during startup."],"exampleFix":"// before — custom policy can report SUCCESS with zero healthy dbs\npublic Decision evaluate(ctx) {\n  return ctx.getTotalCount() > 0 ? Decision.SUCCESS : Decision.FAIL;\n}\n// after — require at least one healthy database\npublic Decision evaluate(ctx) {\n  return ctx.getHealthyCount() > 0 ? Decision.SUCCESS : Decision.FAIL;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  client = MultiDbClient.builder().multiDbConfig(cfg).build();\n} catch (JedisConnectionException e) {\n  if (e.getMessage().contains(\"No healthy database available after initialization\")) {\n    // transient race — rebuild after a short delay\n    Thread.sleep(1000);\n    client = MultiDbClient.builder().multiDbConfig(cfg).build();\n  }\n}","preventionTips":["If implementing a custom InitializationPolicy, make SUCCESS require getHealthyCount() > 0.","Avoid flapping health thresholds that let statuses flip during startup.","Report persistent occurrences as a bug — the library marks this path as unreachable when the policy succeeds correctly.","Keep at least one endpoint free of aggressive health probes so it defaults to HEALTHY."],"tags":["redis","jedis","connection","multi-db","race-condition","health-check"],"backgroundTag":"internal-invariant-violation","analyzedSha":"6dac31d4c224fb3257c216f3985340c6f500cdcb","analyzedAt":"2026-09-08T04:55:01.204Z","contentChangedAt":"2026-09-08T04:55:01.204Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}