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
- 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.
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
- Call connect() exactly once at app bootstrap.
- Memoize the connect promise and share it across all callers.
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
- Attempted execution on released RedisSentinelClient lease
- Cluster closed
- pubSubProxy: didn't define node to do pubsub against
- RedisSentinelClient lease already released
- Client Side Caching is only supported with RESP3
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)