{"record":{"id":"f75c3381fd4193b8","repo":"redis/node-redis","slug":"socket-already-opened","errorCode":null,"errorMessage":"Socket already opened","messagePattern":"Socket already opened","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/client/socket.ts","lineNumber":237,"sourceCode":"      return cause;\n    } else if (retryIn instanceof Error) {\n      this.#isOpen = false;\n      publish(CHANNELS.ERROR, () => ({\n        error: cause,\n        origin: 'client',\n        internal: false,\n        clientId: this.#clientId\n      }));\n      this.emit('error', cause);\n      return new ReconnectStrategyError(retryIn, cause);\n    }\n\n    return retryIn;\n  }\n\n  async connect(): Promise<void> {\n    if (this.#isOpen) {\n      throw new Error('Socket already opened');\n    }\n\n    this.#isOpen = true;\n    return this.#connect();\n  }\n\n  async #connect(): Promise<void> {\n    let retries = 0;\n    do {\n      try {\n        const connectStartTime = performance.now();\n        const socket = this.#socket = await this.#createSocket();\n        this.emit('connect');\n\n        try {\n          await this.#initiateWhileSocketAlive(socket);\n\n          // Check if socket was closed/destroyed during initiator execution","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/client/socket.ts#L219-L255","documentation":"Thrown by RedisSocket.connect() when #isOpen is already true. The socket permits exactly one connect() call per lifecycle; a second call indicates a logic error such as calling client.connect() twice or calling it on a client that auto-connected.","triggerScenarios":"Calling await client.connect() a second time without an intervening close()/destroy(). Calling connect() inside a reconnect handler on a socket that is already open. Constructing a client with a url and calling connect() after the client already connected lazily for a pub/sub subscription.","commonSituations":"Hot-reload / HMR in dev frameworks double-invoking an init function; mis-ordered startup where connect() is called from two code paths; sharing a singleton client across modules where both call connect().","solutions":["Guard the connect call: if (!client.isOpen) await client.connect().","Centralize client creation and connection in one place and export the ready promise.","Call client.disconnect() or client.destroy() before re-connecting if you truly need a fresh connection.","Use a singleton pattern so connect() runs exactly once at app boot."],"exampleFix":"// before\nawait client.connect();\nawait client.connect(); // throws 'Socket already opened'\n\n// after\nif (!client.isOpen) {\n  await client.connect();\n}","handlingStrategy":"validation","validationCode":"if (client.isOpen) {\n  // already connected, skip\n} else {\n  await client.connect();\n}","typeGuard":"function shouldConnect(client: RedisClientType): boolean {\n  return !client.isOpen;\n}","tryCatchPattern":"try {\n  await client.connect();\n} catch (e) {\n  if (e instanceof Error && e.message === 'Socket already opened') return;\n  throw e;\n}","preventionTips":["Guard every connect() call with if (!client.isOpen).","Create and connect the client in exactly one bootstrap location.","Export a ready promise rather than letting multiple callers connect()."],"tags":["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"}