{"record":{"id":"1fdda211cb6fae9f","repo":"redis/node-redis","slug":"already-attempting-to-open","errorCode":null,"errorMessage":"already attempting to open","messagePattern":"already attempting to open","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/sentinel/index.ts","lineNumber":988,"sourceCode":"   *          if the client was immediately ready or no longer exists\n   */\n  releaseClientLease(clientInfo: ClientInfo) {\n    const client = this.#masterClients[clientInfo.id];\n    // client can be undefined if releasing in middle of a reconfigure\n    if (client !== undefined) {\n      const dirtyPromise = client.resetIfDirty();\n      if (dirtyPromise) {\n        return dirtyPromise\n          .then(() => this.#masterClientQueue.push(clientInfo.id));\n      }\n    }\n\n    this.#masterClientQueue.push(clientInfo.id);\n  }\n\n  async connect() {\n    if (this.#isOpen) {\n      throw new Error(\"already attempting to open\")\n    }\n\n    try {\n      this.#isOpen = true;\n\n      this.#connectPromise = this.#connect();\n      await this.#connectPromise;\n      this.#isReady = true;\n    } catch (err) {\n      // The initial connect gave up. Tear down whatever was created along the\n      // way: clients whose first connection attempt failed keep reconnecting\n      // per their `reconnectStrategy`, and would otherwise stay alive in the\n      // background (holding sockets and timers) after `connect()` rejected.\n      this.#connectPromise = undefined;\n      await this.destroy();\n      throw err;\n    } finally {\n      this.#connectPromise = undefined;","sourceCodeStart":970,"sourceCodeEnd":1006,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/sentinel/index.ts#L970-L1006","documentation":"`connect()` flips an internal `#isOpen` latch to true at entry and only clears it on full failure; a second `connect()` call while the first is still in flight (or already succeeded) is treated as a programmer error rather than a no-op. The latch is not relaxed to an idempotent connect, so callers must serialize connection establishment.","triggerScenarios":"Calling `sentinel.connect()` twice in parallel (e.g. two awaiters), or calling it again after a successful connect without checking readiness. Also reachable if a reconnect helper invokes connect() while the original is still resolving.","commonSituations":"Multiple request handlers racing to connect on first use; hot-reload re-running initialization; an `ensureConnected()` helper without its own guard that runs concurrently.","solutions":["Cache the connect promise and reuse it: `this.connectP ??= sentinel.connect()`.","Check `sentinel.isReady` (or track your own opened flag) before calling connect().","Centralize connection startup in one place (app bootstrap) so connect() runs exactly once."],"exampleFix":"// before\nawait sentinel.connect(); // called from two call sites concurrently\n// after\nconst ready = sentinel.isReady;\nif (!ready && !this.connectP) this.connectP = sentinel.connect();\nawait this.connectP;","handlingStrategy":"validation","validationCode":"let connectP: Promise<void> | undefined;\nasync function ensureOpen(sentinel: { isReady: boolean; connect(): Promise<void> }) {\n  if (sentinel.isReady) return;\n  if (!connectP) connectP = sentinel.connect();\n  await connectP;\n  connectP = undefined;\n}","typeGuard":"function isOpen(s: { isReady?: boolean }): boolean {\n  return Boolean(s.isReady);\n}","tryCatchPattern":"try {\n  await sentinel.connect();\n} catch (e) {\n  if (String(e).includes('already attempting to open')) {\n    // another caller is connecting; await readiness via event/getter instead\n    return waitForReady(sentinel);\n  }\n  throw e;\n}","preventionTips":["Call connect() exactly once at app bootstrap.","Memoize the connect promise and share it across all callers."],"tags":["sentinel","lifecycle","concurrency","connect"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}