redis/node-redis · error · Error

Cluster already open

Error message

Cluster already open

What it means

Thrown by RedisClusterSlots.connect() when #isOpen is already true. The cluster permits exactly one connect() call per lifecycle; a second call indicates the cluster was already connected or is mid-connect.

Solutions

  1. Guard with if (!cluster.isOpen) await cluster.connect().
  2. Centralize cluster creation and connect() in a single startup module.
  3. Call cluster.disconnect() before re-connecting if you genuinely need a fresh topology load.
  4. Share the connected cluster instance rather than re-connecting per module.

Example fix

// before
await cluster.connect();
await cluster.connect(); // throws 'Cluster already open'

// after
if (!cluster.isOpen) {
  await cluster.connect();
}
Defensive patterns

Strategy: validation

Validate before calling

if (!cluster.isOpen) {
  await cluster.connect();
}

Type guard

function shouldConnectCluster(cluster: RedisClusterType): boolean {
  return !cluster.isOpen;
}

Try / catch

try {
  await cluster.connect();
} catch (e) {
  if (e instanceof Error && e.message === 'Cluster already open') return;
  throw e;
}

Prevention

When it happens

Trigger: Calling await cluster.connect() twice without an intervening cluster.disconnect()/destroy(). Calling connect() from two modules that share the cluster instance. Calling connect() after the cluster auto-connected during the first command.

Common situations: Hot-reload double-running init code; multiple bootstrap modules each calling connect() on a shared singleton cluster; calling connect() in a retry loop after a partial success.

Related errors


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

Appendix: source

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

    this.#himportRegistry = options.himportRegistry ?? new FieldsetRegistry();
    this.#clusterClientId = clusterClientId;
    this.#reconnectionTracker = new ClusterReconnectionTracker(options.topologyRefreshOnReconnectionAttemptStrategy);

    if (options?.clientSideCache) {
      if (options.clientSideCache instanceof PooledClientSideCacheProvider) {
        this.clientSideCache = options.clientSideCache;
      } else {
        this.clientSideCache = new BasicPooledClientSideCache(options.clientSideCache)
      } 
    }

    this.#clientFactory = RedisClient.factory(this.#options);
    this.#emit = emit;
  }

  async connect() {
    if (this.#isOpen) {
      throw new Error('Cluster already open');
    }

    this.#isOpen = true;
    this.#isReady = false;
    try {
      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;
    }

View on GitHub (pinned to 90fd0652bc)