{"record":{"id":"b111125a3b32aa20","repo":"redis/node-redis","slug":"cluster-already-open","errorCode":null,"errorMessage":"Cluster already open","messagePattern":"Cluster already open","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/cluster-slots.ts","lineNumber":301,"sourceCode":"    this.#himportRegistry = options.himportRegistry ?? new FieldsetRegistry();\n    this.#clusterClientId = clusterClientId;\n    this.#reconnectionTracker = new ClusterReconnectionTracker(options.topologyRefreshOnReconnectionAttemptStrategy);\n\n    if (options?.clientSideCache) {\n      if (options.clientSideCache instanceof PooledClientSideCacheProvider) {\n        this.clientSideCache = options.clientSideCache;\n      } else {\n        this.clientSideCache = new BasicPooledClientSideCache(options.clientSideCache)\n      } \n    }\n\n    this.#clientFactory = RedisClient.factory(this.#options);\n    this.#emit = emit;\n  }\n\n  async connect() {\n    if (this.#isOpen) {\n      throw new Error('Cluster already open');\n    }\n\n    this.#isOpen = true;\n    this.#isReady = false;\n    try {\n      await this.#discoverWithRootNodes();\n      // `destroy()` may have run while discovery was in flight; if so, this\n      // resolution is stale and must not resurrect readiness for a session\n      // that's already been torn down.\n      if (this.#isOpen) {\n        this.#isReady = true;\n        this.#emit('connect');\n      }\n    } catch (err) {\n      this.#isOpen = false;\n      this.#isReady = false;\n      throw err;\n    }","sourceCodeStart":283,"sourceCodeEnd":319,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/cluster/cluster-slots.ts#L283-L319","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guard with if (!cluster.isOpen) await cluster.connect().","Centralize cluster creation and connect() in a single startup module.","Call cluster.disconnect() before re-connecting if you genuinely need a fresh topology load.","Share the connected cluster instance rather than re-connecting per module."],"exampleFix":"// before\nawait cluster.connect();\nawait cluster.connect(); // throws 'Cluster already open'\n\n// after\nif (!cluster.isOpen) {\n  await cluster.connect();\n}","handlingStrategy":"validation","validationCode":"if (!cluster.isOpen) {\n  await cluster.connect();\n}","typeGuard":"function shouldConnectCluster(cluster: RedisClusterType): boolean {\n  return !cluster.isOpen;\n}","tryCatchPattern":"try {\n  await cluster.connect();\n} catch (e) {\n  if (e instanceof Error && e.message === 'Cluster already open') return;\n  throw e;\n}","preventionTips":["Guard every cluster.connect() with if (!cluster.isOpen).","Centralize cluster creation and connection in one bootstrap module.","Share the connected cluster singleton across consumers."],"tags":["cluster","lifecycle","connection","configuration"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}