redis/node-redis · critical · RootNodesUnavailableError

All the root nodes are unavailable

Error message

All the root nodes are unavailable

What it means

RootNodesUnavailableError, thrown at the end of #discoverWithRootNodes after every configured root node was tried and none returned a valid cluster topology. This means initial cluster connection (or a full rediscovery) could not reach any seed node. It surfaces as a typed error so callers can distinguish 'all roots down' from a single-node failure.

Solutions

  1. Verify each rootNode host/port is reachable from the client host (telnet/nc/redis-cli ping).
  2. Check network ACLs, security groups, and firewall rules permit TCP to the Redis ports.
  3. Confirm DNS resolves the rootNode hostnames and the IPs are current.
  4. Add redundant root nodes so a single unreachable seed does not cause total failure.
  5. Retry cluster.connect() after a backoff in case the outage is transient.

Example fix

// before
const cluster = createCluster({ rootNodes: [{ socket: { host: 'wrong-host', port: 6379 } }] });
await cluster.connect(); // RootNodesUnavailableError

// after
const cluster = createCluster({
  rootNodes: [
    { socket: { host: 'redis-0.cluster', port: 6379 } },
    { socket: { host: 'redis-1.cluster', port: 6379 } }
  ]
});
await cluster.connect();
Defensive patterns

Strategy: retry

Validate before calling

async function canReachRoot(host: string, port: number): Promise<boolean> {
  try {
    const s = await new Promise<import('net').Socket>((res, rej) => {
      const sock = require('net').connect(port, host, () => res(sock));
      sock.on('error', rej);
    });
    s.destroy();
    return true;
  } catch { return false; }
}

Type guard

function isRootNodesUnavailable(e: unknown): boolean {
  return e instanceof RootNodesUnavailableError;
}

Try / catch

for (let i = 0; i < 5; i++) {
  try {
    await cluster.connect();
    break;
  } catch (e) {
    if (e instanceof RootNodesUnavailableError) { await new Promise(r => setTimeout(r, 1000 * (i + 1))); continue; }
    throw e;
  }
}

Prevention

When it happens

Trigger: cluster.connect() where every entry in rootNodes is unreachable (wrong host/port, firewall, DNS failure, all nodes down). Also thrown on a full topology refresh if all roots are unreachable at that point.

Common situations: Misconfigured rootNodes host/port; Redis cluster behind a firewall/security group blocking the client; DNS not resolving cluster endpoints; all seed nodes temporarily down during a major outage; localhost vs 127.0.0.1 mismatch in containers.

Related errors


AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11). Data as JSON: /api/errors/5eca4ef2a745ef16. Report an issue: GitHub.

Appendix: source

Thrown at packages/client/lib/cluster/cluster-slots.ts:338

  }

  async #discoverWithRootNodes() {
    const start = Math.floor(Math.random() * this.#options.rootNodes.length);
    for (let i = start; i < this.#options.rootNodes.length; i++) {
      if (!this.#isOpen) throw new Error('Cluster closed');
      if (await this.#discover(this.#options.rootNodes[i])) {
        return;
      }
    }

    for (let i = 0; i < start; i++) {
      if (!this.#isOpen) throw new Error('Cluster closed');
      if (await this.#discover(this.#options.rootNodes[i])) {
        return;
      }
    }

    throw new RootNodesUnavailableError();
  }

  #resetSlots() {
    this.slots = new Array(RedisClusterSlots.#SLOTS);
    this.masters = [];
    this.replicas = [];
    this._randomNodeIterator = undefined;
  }

  async #discover(rootNode: RedisClusterClientOptions) {
    this.clientSideCache?.clear();
    this.clientSideCache?.disable();

    try {
      const addressesInUse = new Set<string>(),
        promises: Array<Promise<unknown>> = [],
        eagerConnect = this.#options.minimizeConnections !== true;

View on GitHub (pinned to 90fd0652bc)