redis/node-redis · error · Error
Cluster closed
Error message
Cluster closed
What it means
Thrown from RedisClusterSlots.#discoverWithRootNodes (cluster-slots.ts:256) inside the first half of the root-node scan loop, when #isOpen became false during an in-flight connect()/discovery. This means close()/destroy() ran concurrently with connect() — the discovery honors the teardown and aborts instead of resurrecting a closed cluster.
Source
Thrown at packages/client/lib/cluster/cluster-slots.ts:256
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 bb5beb5657)
Solutions
- Serialize connect()/close(): do not destroy a cluster whose connect() has not resolved.
- Await connect() (or its rejection) before calling close()/destroy().
- In tests, await connect().catch(() => {}) in afterEach before destroying.
Example fix
// before
const p = cluster.connect();
cluster.destroy(); // races with discovery -> 'Cluster closed'
await p;
// after
try { await cluster.connect(); } catch {}
await cluster.destroy(); Defensive patterns
Strategy: try-catch
Type guard
function isClusterClosed(e: unknown): boolean {
return e instanceof Error && /Cluster closed/i.test(e.message);
} Try / catch
try { await cluster.connect(); }
catch (e) { if (isClusterClosed(e)) { /* expected: concurrent teardown */ return; } throw e; } Prevention
- Await connect() before calling close()/destroy().
- Serialize startup and shutdown with a single lifecycle owner.
- In tests, await connect().catch(()=>{}) in cleanup before destroy().
When it happens
Trigger: cluster.connect() is in progress (iterating root nodes) and another code path calls cluster.close() or destroy() mid-discovery.
Common situations: Shutdown racing with startup; a health check starting a connect that is immediately cancelled; tests that tear down the cluster before discovery finishes.
Related errors
- Cluster already open
- All the root nodes are unavailable
- The client is closed
- The client is offline
- Request policy ${requestPolicy} produced no target nodes
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/8658a9c98287075a.json.
Report an issue: GitHub.