redis/node-redis · warning · ClientOfflineError

The client is offline

Error message

The client is offline

What it means

Thrown by RedisClusterSlots.#assertReady() when #isOpen is true but #isReady is false. The cluster is open (connect() was called) but has not finished discovering topology and establishing node clients — or it lost readiness during a rediscovery. Commands are rejected because the slot map is not yet populated enough to route correctly.

Solutions

  1. Await cluster.connect() fully and listen for the 'ready' event before issuing commands.
  2. Guard with if (cluster.isReady) or retry with backoff until isReady is true.
  3. Ensure connect() did not throw; if it did, re-connect before issuing commands.
  4. During topology refreshes, queue or retry commands rather than assuming continuous readiness.

Example fix

// before
cluster.connect(); // not awaited
cluster.set('k', 'v'); // throws ClientOfflineError

// after
await cluster.connect(); // resolves when ready
if (!cluster.isReady) await once(cluster, 'ready');
await cluster.set('k', 'v');
Defensive patterns

Strategy: validation

Validate before calling

if (!cluster.isReady) {
  await once(cluster, 'ready');
}

Type guard

function clusterIsReady(cluster: RedisClusterType): boolean {
  return cluster.isReady;
}

Try / catch

while (!cluster.isReady) {
  await new Promise(r => setTimeout(r, 50));
}
await cluster.set('k', 'v');

Prevention

When it happens

Trigger: Issuing a command immediately after cluster.connect() resolves but before the 'ready'/'connect' event fires (rare). Issuing commands during a topology refresh that temporarily unset #isReady. Issuing commands right after connect() failed partway and left the cluster open but not ready.

Common situations: Not awaiting the connect()/ready event fully; race between command issuance and initial discovery completion; the cluster connect threw after setting #isOpen but the caller continued issuing commands; reconnect logic issuing commands before rediscovery completes.

Related errors


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

Appendix: source

Thrown at packages/client/lib/cluster/cluster-slots.ts:995

      promises.push(fn(this.pubSubNode.client));
      this.pubSubNode = undefined;
    }

    this.#resetSlots();
    this.nodeByAddress.clear();
    this.#reconnectionTracker.clear();

    await Promise.allSettled(promises);
    this.#emit('disconnect');
  }

  #assertReady() {
    if (!this.#isOpen) {
      throw new ClientClosedError();
    }

    if (!this.#isReady) {
      throw new ClientOfflineError();
    }
  }

  /**
   * All fan-out target nodes (masters + replicas), WITHOUT connecting. The
   * caller connects each node lazily in its own per-node promise so a single
   * failed connect rejects only that node's execution — letting reducers such
   * as `one_succeeded` still see the reachable shards — instead of a `Promise.all`
   * over the connects failing the whole route up front. Excludes the dedicated
   * PubSub connection (not in `masters`/`replicas`).
   */
  getAllNodes() {
    this.#assertReady();

    return [...this.masters, ...this.replicas];
  }

  /** Master fan-out target nodes, WITHOUT connecting (see {@link getAllNodes}). */

View on GitHub (pinned to 90fd0652bc)