{"id":"01b6bbdac056a27a","repo":"redis/node-redis","slug":"the-client-is-closed-01b6bb","errorCode":null,"errorMessage":"The client is closed","messagePattern":"The client is closed","errorType":"exception","errorClass":"ClientClosedError","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/cluster-slots.ts","lineNumber":888,"sourceCode":"      promises.push(fn(client));\n    }\n\n    if (this.pubSubNode) {\n      promises.push(fn(this.pubSubNode.client));\n      this.pubSubNode = undefined;\n    }\n\n    this.#resetSlots();\n    this.nodeByAddress.clear();\n    this.#reconnectionTracker.clear();\n\n    await Promise.allSettled(promises);\n    this.#emit('disconnect');\n  }\n\n  #assertReady() {\n    if (!this.#isOpen) {\n      throw new ClientClosedError();\n    }\n\n    if (!this.#isReady) {\n      throw new ClientOfflineError();\n    }\n  }\n\n  /**\n   * All fan-out target nodes (masters + replicas), WITHOUT connecting. The\n   * caller connects each node lazily in its own per-node promise so a single\n   * failed connect rejects only that node's execution — letting reducers such\n   * as `one_succeeded` still see the reachable shards — instead of a `Promise.all`\n   * over the connects failing the whole route up front. Excludes the dedicated\n   * PubSub connection (not in `masters`/`replicas`).\n   */\n  getAllNodes() {\n    this.#assertReady();\n","sourceCodeStart":870,"sourceCodeEnd":906,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/cluster-slots.ts#L870-L906","documentation":"Thrown from RedisClusterSlots.#assertReady (cluster-slots.ts:888) as ClientClosedError when #isOpen is false. assertReady guards every routing method (getAllNodes, getClientForKey, getClientAndSlotNumber, scan, etc.), so any command issued against a cluster that has been closed/destroyed (or never connected) rejects with this.","triggerScenarios":"Issuing a command after cluster.close()/destroy(); issuing a command before connect() resolved; issuing commands during shutdown after the cluster was closed.","commonSituations":"Missing await cluster.connect() at startup; using the cluster after an error handler closed it; shutdown sequence that closes first then drains queued work.","solutions":["Ensure cluster.connect() has resolved before issuing commands (await it at startup).","Do not issue commands after close()/destroy(); gate them on cluster.isOpen.","On fatal errors, recreate the cluster rather than reusing a closed instance."],"exampleFix":"// before\nconst cluster = createCluster({ rootNodes });\nawait cluster.set('k', 'v'); // throws 'The client is closed' — never connected\n\n// after\nconst cluster = createCluster({ rootNodes });\nawait cluster.connect();\nawait cluster.set('k', 'v');","handlingStrategy":"validation","validationCode":"if (!cluster.isOpen) { throw new Error('cluster not open — call connect() first'); }","typeGuard":"import { ClientClosedError } from '@redis/client';\nfunction isClientClosed(e: unknown): boolean {\n  return e instanceof Error && (e instanceof ClientClosedError || /client is closed/i.test(e.message));\n}","tryCatchPattern":null,"preventionTips":["Await cluster.connect() before issuing any command.","Stop sending commands after close()/destroy().","Recreate the cluster after a fatal close rather than reusing it."],"tags":["cluster","lifecycle","connection"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}