{"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":232,"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":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/cluster-slots.ts#L214-L250","documentation":"Thrown from RedisClusterSlots.connect() (cluster-slots.ts:232) when #isOpen is already true. Prevents a second concurrent connect/discovery over an already-open cluster. Surfaced to the caller of cluster.connect().","triggerScenarios":"Calling cluster.connect() twice; awaiting connect() in a loop that already resolved; two callers racing to connect the same cluster instance.","commonSituations":"App startup code plus a health-check both connecting; memoization bug where the connect promise isn't shared; retry wrapper that re-enters connect().","solutions":["Connect once at startup and reuse the instance; memoize the connect() promise if multiple callers need it.","Guard with cluster.isOpen before calling connect().","After a full close()/destroy(), a new connect() is fine — ensure teardown completed first."],"exampleFix":"// before\nawait cluster.connect();\nawait cluster.connect(); // throws 'Cluster already open'\n\n// after\nlet ready;\nfunction ensureConnected() { return ready ??= cluster.connect(); }","handlingStrategy":"validation","validationCode":"if (cluster.isOpen) { /* skip */ } else { await cluster.connect(); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Memoize the connect() promise across callers.","Connect once at startup; reuse the instance.","Guard with cluster.isOpen before connecting."],"tags":["cluster","lifecycle","connection"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}