redis/node-redis · error · Error

Cluster closed

Error message

Cluster closed

What it means

Thrown inside #discoverWithRootNodes during the forward iteration over rootNodes when #isOpen becomes false mid-discovery. This guard ensures that if the cluster is closed/destroyed while initial topology discovery is still trying root nodes, discovery stops immediately rather than continuing to connect to nodes for a cluster that no longer exists.

Solutions

  1. Let the in-flight connect() reject and catch it in your startup code rather than suppressing.
  2. Avoid calling destroy()/disconnect() while connect() is unresolved; await connect() first (or its rejection).
  3. If you must cancel, catch the resulting 'Cluster closed' error and treat it as expected during shutdown.
  4. Reduce connect latency with shorter connectTimeout so discovery fails fast instead of hanging.

Example fix

// before
const p = cluster.connect();
cluster.destroy(); // mid-discovery -> 'Cluster closed'
await p;

// after
try {
  await cluster.connect();
} catch (e) {
  // connect was cancelled or cluster torn down during discovery
  if (!cluster.isOpen) return;
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

function isClusterClosedError(e: unknown): boolean {
  return e instanceof Error && e.message === 'Cluster closed';
}

Try / catch

try {
  await cluster.connect();
} catch (e) {
  if (e instanceof Error && e.message === 'Cluster closed') return; // torn down mid-discovery
  throw e;
}

Prevention

When it happens

Trigger: Calling cluster.connect() and then, while it is still iterating root nodes (e.g., the first root is slow/unreachable), calling cluster.destroy() or cluster.disconnect(). The in-flight discover loop sees #isOpen flip to false and throws.

Common situations: Application startup racing with shutdown (e.g., SIGTERM during slow cluster discovery); a health check timing out and triggering destroy() while connect() is still resolving; test teardown cancelling a slow connect().

Related errors


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

Appendix: source

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

      await this.#discoverWithRootNodes();
      // `destroy()` may have run while discovery was in flight; if so, this
      // resolution is stale and must not resurrect readiness for a session
      // that's already been torn down.
      if (this.#isOpen) {
        this.#isReady = true;
        this.#emit('connect');
      }
    } catch (err) {
      this.#isOpen = false;
      this.#isReady = false;
      throw err;
    }
  }

  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 = [];

View on GitHub (pinned to 90fd0652bc)