redis/node-redis · error · Error
already attempting to open
Error message
already attempting to open
What it means
Thrown by RedisSentinelInternal.connect() when the `#isOpen` flag is already true. connect() is a one-shot lifecycle entry point: it sets #isOpen, drives #connect(), and only clears it in the finally block. Calling connect() a second time while the first is still in flight (or after a successful open) violates the state machine and is rejected immediately (sentinel/index.ts:987).
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 bb5beb5657)
Solutions
- Await the original connect() promise and store it; reuse it instead of calling connect() again.
- Guard with `if (!sentinel.isOpen) await sentinel.connect();` before connecting.
- If you need a fresh connection after an error, call destroy() first, then connect().
- Memoize the connect promise at the call site: `this._ready ??= sentinel.connect();`.
Example fix
// before
await sentinel.connect(); // may run twice on retry
// after
if (!sentinel.isOpen) {
await sentinel.connect();
} Defensive patterns
Strategy: validation
Validate before calling
let readyPromise: Promise<void> | undefined;
async function ensureOpen(sentinel) {
if (sentinel.isOpen) return;
readyPromise ??= sentinel.connect();
try {
await readyPromise;
} finally {
readyPromise = undefined;
}
} Type guard
const isOpen = (s: { isOpen: boolean }): boolean => s.isOpen; Prevention
- Memoize the connect() promise so concurrent callers await the same one.
- Guard every connect() call site with `if (!sentinel.isOpen)`.
- Call destroy() before re-connecting on a recovered error.
- Never put connect() inside an automatic retry loop that could overlap calls.
When it happens
Trigger: Calling sentinel.connect() twice without awaiting the first; calling connect() again after it already resolved; a reconnect/retry wrapper that invokes connect() on top of an already-open client.
Common situations: Retry/early-out wrappers around connect() that don't track the promise; calling connect() in both a bootstrap function and a health check; tests that reuse a client instance across cases.
Related errors
- Cluster closed
- Attempted execution on released RedisSentinelClient lease
- RedisSentinelClient lease already released
- TokenManager is not running, but refresh was called
- TokenManager is not running, but a new token was received
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/1fdda211cb6fae9f.json.
Report an issue: GitHub.