redis/node-redis · error · Error
RedisSentinelClient lease already released
Error message
RedisSentinelClient lease already released
What it means
release() was called on a RedisSentinelClient lease that was already released. The #clientInfo field is already undefined (set by the first release), so a second call is rejected to prevent corrupting pool state or double-returning the lease.
Solutions
- Guard release with a boolean flag to ensure it is called exactly once
- Use try/finally with release only in the finally block, never in the try body
- Null out the lease reference after release as a visual indicator it is spent
Example fix
// before
const lease = await sentinel.getClient();
await lease.release();
await lease.release(); // throws
// after
const lease = await sentinel.getClient();
let released = false;
try {
// ... work
} finally {
if (!released) {
released = true;
await lease.release();
}
} Defensive patterns
Strategy: validation
Validate before calling
// Guard release with a boolean flag to prevent double-release.
let released = false;
const lease = await sentinel.getClient();
function safeRelease() {
if (released) return;
released = true;
return lease.release();
}
try {
// ... work
} finally {
await safeRelease();
} Try / catch
try {
await lease.release();
} catch (e) {
if (e.message === 'RedisSentinelClient lease already released') {
// Already released — safe to ignore
}
} Prevention
- Call release() exactly once, in a finally block
- Use a boolean flag to guard against double-release in error paths
- Null out the lease reference after release as a visual indicator
- Avoid releasing in both the try body and the finally block
When it happens
Trigger: Calling lease.release() twice — e.g., an explicit release followed by a finally block that calls release again; error-handling code that releases then the normal path also releases.
Common situations: try/finally where release is in the finally and also in the try body; cleanup logic in multiple error handlers both calling release; async race where two code paths release the same lease.
Related errors
- Attempted execution on released RedisSentinelClient lease
- already attempting to open
- pubSubProxy: didn't define node to do pubsub against
- Client Side Caching is only supported with RESP3
- Cluster already open
AI-assisted analysis of redis/node-redis@90fd0652bc (2026-08-11).
Data as JSON: /api/errors/034df378d716a0e8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/client/lib/sentinel/index.ts:277
client => client.unwatch()
)
}
unwatch = this.UNWATCH;
/**
* Releases the client lease back to the pool
*
* After calling this method, the client instance should no longer be used as it
* will be returned to the client pool and may be given to other operations.
*
* @returns A promise that resolves when the client is ready to be reused, or undefined
* if the client was immediately ready
* @throws Error if the lease has already been released
*/
release() {
if (this._self.#clientInfo === undefined) {
throw new Error('RedisSentinelClient lease already released');
}
const result = this._self.#internal.releaseClientLease(this._self.#clientInfo);
this._self.#clientInfo = undefined;
return result;
}
}
export default class RedisSentinel<
M extends RedisModules,
F extends RedisFunctions,
S extends RedisScripts,
RESP extends RespVersions,
TYPE_MAPPING extends TypeMapping
> extends EventEmitter {
readonly _self: RedisSentinel<M, F, S, RESP, TYPE_MAPPING>;
#internal: RedisSentinelInternal<M, F, S, RESP, TYPE_MAPPING>;View on GitHub (pinned to 90fd0652bc)