{"id":"23cec5d3c4b74a67","repo":"redis/node-redis","slug":"no-available-replicas","errorCode":null,"errorMessage":"no available replicas","messagePattern":"no available replicas","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/sentinel/index.ts","lineNumber":1827,"sourceCode":"        }\n\n        return replicas;\n      } finally {\n        client.destroy();\n      }\n    }\n\n    if (connected) {\n      throw new Error(\"No Replicas Nodes Enumerated\");\n    }\n\n    throw new Error(\"couldn't connect to any sentinels\");\n  }\n\n  async getReplicaClient() {\n    const replicas = await this.getReplicaNodes();\n    if (replicas.length == 0) {\n      throw new Error(\"no available replicas\");\n    }\n\n    this.#replicaIdx++;\n    if (this.#replicaIdx >= replicas.length) {\n      this.#replicaIdx = 0;\n    }\n\n    const replica = replicas[this.#replicaIdx];\n    const socket = getMappedNode(replica.host, replica.port, this.options.nodeAddressMap);\n    return RedisClient.create({\n      ...this.options.nodeClientOptions,\n      socket: {\n        ...this.options.nodeClientOptions?.socket,\n        host: socket.host,\n        port: socket.port\n      }\n    });\n  }","sourceCodeStart":1809,"sourceCodeEnd":1845,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/sentinel/index.ts#L1809-L1845","documentation":"Defensive guard in RedisSentinelFactory.getReplicaClient() that throws if getReplicaNodes() somehow returned an empty array (sentinel/index.ts:1826). In normal flow getReplicaNodes() already throws error 88 or 89 before returning empty, so this is a belt-and-suspenders check against future logic changes that return [] without throwing.","triggerScenarios":"Effectively unreachable today; would only fire if getReplicaNodes() were changed to return an empty array instead of throwing, or if the array were mutated to empty between the call and the length check.","commonSituations":"Not expected in normal usage; treat as a sanity assertion. If seen, suspect a regression in getReplicaNodes or concurrent mutation.","solutions":["Treat as an assertion failure — report it with the node-redis version and steps to reproduce.","In the meantime, ensure replicas are provisioned so getReplicaNodes() returns a non-empty list.","Fall back to getMasterClient() for reads if replica reads are optional."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Defensive: call getReplicaNodes() yourself first and only build a client when non-empty.\nconst replicas = await factory.getReplicaNodes(); // throws 88/89 with clear cause\nif (replicas.length === 0) throw new Error('no replicas available');\nconst client = await factory.getReplicaClient();","typeGuard":null,"tryCatchPattern":"try {\n  const replicaClient = await factory.getReplicaClient();\n} catch (e) {\n  if (e instanceof Error && /no available replicas/.test(e.message)) {\n    // assertion-style fallback: use the master client for reads\n    return factory.getMasterClient();\n  }\n  throw e;\n}","preventionTips":["Treat this as an assertion; if it fires, report the node-redis version.","Prefer getMasterClient() for reads when replica availability is optional.","Ensure replicas are provisioned so the path returns a valid client."],"tags":["sentinel","replicas","defensive","factory"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}