{"id":"5eca4ef2a745ef16","repo":"redis/node-redis","slug":"all-the-root-nodes-are-unavailable","errorCode":null,"errorMessage":"All the root nodes are unavailable","messagePattern":"All the root nodes are unavailable","errorType":"exception","errorClass":"RootNodesUnavailableError","httpStatus":null,"severity":"critical","filePath":"packages/client/lib/cluster/cluster-slots.ts","lineNumber":269,"sourceCode":"  }\n\n  async #discoverWithRootNodes() {\n    const start = Math.floor(Math.random() * this.#options.rootNodes.length);\n    for (let i = start; i < this.#options.rootNodes.length; i++) {\n      if (!this.#isOpen) throw new Error('Cluster closed');\n      if (await this.#discover(this.#options.rootNodes[i])) {\n        return;\n      }\n    }\n\n    for (let i = 0; i < start; i++) {\n      if (!this.#isOpen) throw new Error('Cluster closed');\n      if (await this.#discover(this.#options.rootNodes[i])) {\n        return;\n      }\n    }\n\n    throw new RootNodesUnavailableError();\n  }\n\n  #resetSlots() {\n    this.slots = new Array(RedisClusterSlots.#SLOTS);\n    this.masters = [];\n    this.replicas = [];\n    this._randomNodeIterator = undefined;\n  }\n\n  async #discover(rootNode: RedisClusterClientOptions) {\n    this.clientSideCache?.clear();\n    this.clientSideCache?.disable();\n\n    try {\n      const addressesInUse = new Set<string>(),\n        promises: Array<Promise<unknown>> = [],\n        eagerConnect = this.#options.minimizeConnections !== true;\n","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/cluster-slots.ts#L251-L287","documentation":"Thrown as RootNodesUnavailableError (cluster-slots.ts:269) after #discoverWithRootNodes exhausts every root node without one succeeding. Each root node either failed to connect or failed to return slot topology. This is the hard failure of cluster.connect() — the cluster cannot be used until at least one root node is reachable.","triggerScenarios":"cluster.connect() where every entry in rootNodes is unreachable (wrong host/port, firewalled, DNS failure, all nodes down, auth failing on all).","commonSituations":"Wrong root node addresses in config; network/firewall blocking the Redis port; all cluster nodes down during a full outage; credentials rotated so AUTH fails on every node; pointing at a standalone Redis instead of a cluster (CLUSTER SLOTS fails).","solutions":["Verify root node host/port reachability: telnet/nc to each node, and confirm CLUSTER INFO returns cluster_enabled:1.","Check AUTH/credentials and TLS settings match across all root nodes.","Retry connect() with backoff — a transient full outage may clear.","Ensure at least one root node is a cluster-enabled Redis (not a standalone or sentinel node)."],"exampleFix":"// before\nconst cluster = createCluster({ rootNodes: [{ socket: { host: 'wrong-host', port: 7000 } }] });\nawait cluster.connect(); // RootNodesUnavailableError\n\n// after\nconst cluster = createCluster({\n  rootNodes: [\n    { socket: { host: 'node-0.cluster', port: 7000 } },\n    { socket: { host: 'node-1.cluster', port: 7000 } }\n  ]\n});\nawait cluster.connect();","handlingStrategy":"retry","validationCode":"import net from 'node:net';\nasync function reachable(host: string, port: number, ms = 1000): Promise<boolean> {\n  return new Promise(res => {\n    const s = net.connect({ host, port });\n    s.setTimeout(ms);\n    s.on('connect', () => { s.destroy(); res(true); });\n    s.on('error', () => res(false));\n    s.on('timeout', () => { s.destroy(); res(false); });\n  });\n}\n// before connect: assert at least one root node is reachable","typeGuard":"import { RootNodesUnavailableError } from '@redis/client';\nfunction isRootNodesUnavailable(e: unknown): boolean {\n  return e instanceof Error && (e instanceof RootNodesUnavailableError || /root nodes are unavailable/i.test(e.message));\n}","tryCatchPattern":"for (let i = 0; i < 5; i++) {\n  try { await cluster.connect(); break; }\n  catch (e) { if (isRootNodesUnavailable(e) && i < 4) { await sleep(2 ** i * 500); continue; } throw e; }\n}","preventionTips":["Validate root node addresses and ports before startup.","Confirm each root node is cluster-enabled (CLUSTER INFO).","Verify AUTH/TLS config matches all root nodes.","Add startup retry with backoff for transient full outages."],"tags":["cluster","connection","startup","network"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}