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
- Let the in-flight connect() reject and catch it in your startup code rather than suppressing.
- Avoid calling destroy()/disconnect() while connect() is unresolved; await connect() first (or its rejection).
- If you must cancel, catch the resulting 'Cluster closed' error and treat it as expected during shutdown.
- 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
- Await connect() before initiating destroy()/disconnect().
- Treat 'Cluster closed' from connect() as expected during shutdown.
- Use a short connectTimeout so discovery fails fast.
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
- All the root nodes are unavailable
- already attempting to open
- Cluster already open
- FT.CURSOR: unknown cursor
- The client is closed
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)