redis/node-redis · error · Error

already attempting to open

Error message

already attempting to open

What it means

`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.

Solutions

  1. Cache the connect promise and reuse it: `this.connectP ??= sentinel.connect()`.
  2. Check `sentinel.isReady` (or track your own opened flag) before calling connect().
  3. Centralize connection startup in one place (app bootstrap) so connect() runs exactly once.

Example fix

// before
await sentinel.connect(); // called from two call sites concurrently
// after
const ready = sentinel.isReady;
if (!ready && !this.connectP) this.connectP = sentinel.connect();
await this.connectP;
Defensive patterns

Strategy: validation

Validate before calling

let connectP: Promise<void> | undefined;
async function ensureOpen(sentinel: { isReady: boolean; connect(): Promise<void> }) {
  if (sentinel.isReady) return;
  if (!connectP) connectP = sentinel.connect();
  await connectP;
  connectP = undefined;
}

Type guard

function isOpen(s: { isReady?: boolean }): boolean {
  return Boolean(s.isReady);
}

Try / catch

try {
  await sentinel.connect();
} catch (e) {
  if (String(e).includes('already attempting to open')) {
    // another caller is connecting; await readiness via event/getter instead
    return waitForReady(sentinel);
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: Multiple request handlers racing to connect on first use; hot-reload re-running initialization; an `ensureConnected()` helper without its own guard that runs concurrently.

Related errors


AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11). Data as JSON: /api/errors/1fdda211cb6fae9f. Report an issue: GitHub.

Appendix: source

Thrown at packages/client/lib/sentinel/index.ts:988

   *          if the client was immediately ready or no longer exists
   */
  releaseClientLease(clientInfo: ClientInfo) {
    const client = this.#masterClients[clientInfo.id];
    // client can be undefined if releasing in middle of a reconfigure
    if (client !== undefined) {
      const dirtyPromise = client.resetIfDirty();
      if (dirtyPromise) {
        return dirtyPromise
          .then(() => this.#masterClientQueue.push(clientInfo.id));
      }
    }

    this.#masterClientQueue.push(clientInfo.id);
  }

  async connect() {
    if (this.#isOpen) {
      throw new Error("already attempting to open")
    }

    try {
      this.#isOpen = true;

      this.#connectPromise = this.#connect();
      await this.#connectPromise;
      this.#isReady = true;
    } catch (err) {
      // The initial connect gave up. Tear down whatever was created along the
      // way: clients whose first connection attempt failed keep reconnecting
      // per their `reconnectStrategy`, and would otherwise stay alive in the
      // background (holding sockets and timers) after `connect()` rejected.
      this.#connectPromise = undefined;
      await this.destroy();
      throw err;
    } finally {
      this.#connectPromise = undefined;

View on GitHub (pinned to 90fd0652bc)