redis/node-redis · error · ClientClosedError
The client is closed
Error message
The client is closed
What it means
ClientClosedError (message 'The client is closed') is thrown/rejected from the command-execution funnels — sendCommand (index.ts:1626), _executePipeline (1861), _executeMulti (1924) — and from the socket connect path (socket.ts) when the socket is not open. It signals that the client has been closed (or never connected) and will not accept commands. The same error class is used across all entry points so callers can branch on `err instanceof ClientClosedError` uniformly.
Source
Thrown at packages/client/lib/client/index.ts:1924
);
}
/**
* @internal
*/
async _executeMulti(
commands: Array<RedisMultiQueuedCommand>,
selectedDB?: number
) {
assertNoHimportSessionCommands(commands);
const dirtyWatch = this._self.#dirtyWatch;
this._self.#dirtyWatch = undefined;
const watchEpoch = this._self.#watchEpoch;
this._self.#watchEpoch = undefined;
if (!this._self.#socket.isOpen) {
throw new ClientClosedError();
}
if (dirtyWatch) {
throw new WatchError(dirtyWatch);
}
if (watchEpoch && watchEpoch !== this._self.socketEpoch) {
throw new WatchError('Client reconnected after WATCH');
}
const batchSize = commands.length;
return trace(CHANNELS.TRACE_BATCH,
async () => {
const typeMapping = this._commandOptions?.typeMapping;
const chainId = Symbol('MULTI Chain');
const promises: Array<Promise<unknown>> = [
this._self.#queue.addCommand(['MULTI'], { chainId }),View on GitHub (pinned to bb5beb5657)
Solutions
- Call `await client.connect()` before issuing commands, and guard command code with `if (client.isOpen)`.
- After `client.close()`, do not reuse the instance — create a new client (or call connect() again if appropriate).
- Handle ClientClosedError in your command path and reconnect/recreate the client.
- In long-running services, connect once at startup and close only at shutdown.
Example fix
// before
const client = createClient({ url });
await client.get('k'); // throws ClientClosedError
// after
const client = createClient({ url });
await client.connect();
await client.get('k'); Defensive patterns
Strategy: try-catch
Validate before calling
async function safeGet(client, key) {
if (!client.isOpen) {
await client.connect();
}
return client.get(key);
} Type guard
function isClientClosedError(err: unknown): boolean {
return err instanceof Error && err.message === 'The client is closed';
} Try / catch
try {
return await client.get(key);
} catch (err) {
if (err instanceof Error && err.message === 'The client is closed') {
await client.connect();
return client.get(key);
}
throw err;
} Prevention
- Always `await client.connect()` before issuing commands.
- Guard command code with `if (client.isOpen)`.
- Do not reuse a client after close(); create a new one.
- Connect once at process startup; close only at shutdown.
When it happens
Trigger: Calling `client.get('k')` after `await client.close()`; calling any command before `client.connect()`; using a client whose connection dropped and was closed; calling `.exec()` on a multi/pipeline after the client was closed; the socket's reconnect gave up and the client moved to a closed state.
Common situations: Forgetting to `await client.connect()`; using the client after `client.close()` (e.g. in a shutdown handler or after an error); a previous fatal error that closed the client but the caller keeps issuing commands; reusing a client across request handlers without ensuring it is open.
Related errors
- Socket already opened
- Cluster already open
- The client is closed
- The client is offline
- TokenManager is not running, but refresh was called
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/c6f46ec38a10eaab.json.
Report an issue: GitHub.