redis/node-redis · error · Error

Client Side Caching is only supported with RESP3

Error message

Client Side Caching is only supported with RESP3

What it means

Redis Sentinel client-side caching (CSC / tracking) depends on RESP3 server-pushed invalidation messages, so the Sentinel constructor's #validateOptions refuses to enable it under any other protocol. The guard is `options.clientSideCache && (options.RESP ?? DEFAULT_RESP) !== 3`; since DEFAULT_RESP is 3, omitting RESP is fine, but explicitly setting RESP:2 (or any non-3 value) together with clientSideCache is rejected at construction time, before any connection is attempted.

Solutions

  1. Set `RESP: 3` on the same options object that sets `clientSideCache`.
  2. Omit `RESP` entirely so it defaults to 3, which satisfies the guard.
  3. Drop `clientSideCache` if you must stay on RESP:2.

Example fix

// before
new RedisSentinel({ name: 'mymaster', RESP: 2, clientSideCache: { ttl: 1000, maxEntries: 1000 } }, id);
// after
new RedisSentinel({ name: 'mymaster', RESP: 3, clientSideCache: { ttl: 1000, maxEntries: 1000 } }, id);
Defensive patterns

Strategy: validation

Validate before calling

function assertCscResp3(opts: { clientSideCache?: unknown; RESP?: number }) {
  const resp = opts.RESP ?? 3; // DEFAULT_RESP is 3
  if (opts.clientSideCache && resp !== 3) {
    throw new Error(`clientSideCache requires RESP 3, got RESP ${resp}`);
  }
}
assertCscResp3(opts); // before `new RedisSentinel(opts, id)`

Type guard

function isCscOptionsSafe(opts: { clientSideCache?: unknown; RESP?: number }): boolean {
  return !opts.clientSideCache || (opts.RESP ?? 3) === 3;
}

Try / catch

let sentinel;
try {
  sentinel = new RedisSentinel(opts, id);
} catch (e) {
  if (String(e).includes('Client Side Caching is only supported with RESP3')) {
    opts = { ...opts, RESP: 3 };
    sentinel = new RedisSentinel(opts, id);
  } else throw e;
}

Prevention

When it happens

Trigger: Constructing `new RedisSentinel({ clientSideCache: {...}, RESP: 2 }, id)` or `new RedisSentinel({ clientSideCache: cacheProvider, RESP: 2 }, id)`. Triggers in the constructor, synchronously, regardless of whether the server actually supports RESP3.

Common situations: Copying a RESP:2 config from an existing standalone RedisClient into a Sentinel setup; standardizing on RESP:2 fleet-wide then opting into CSC; migrating from a non-Sentinel client that paired RESP:2 with a different caching strategy.

Related errors


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

Appendix: source

Thrown at packages/client/lib/sentinel/index.ts:837

  #connectPromise?: Promise<void>;
  #maxCommandRediscovers: number;
  readonly #pubSubProxy: PubSubProxy;

  #scanTimer?: NodeJS.Timeout

  #destroy = false;

  #trace: (msg: string) => unknown = () => { };

  #clientSideCache?: PooledClientSideCacheProvider;
  get clientSideCache() {
    return this.#clientSideCache;
  }

  #validateOptions(options?: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>) {
    if (options?.clientSideCache && (options?.RESP ?? DEFAULT_RESP) !== 3) {
      throw new Error('Client Side Caching is only supported with RESP3');
    }
  }

  constructor(options: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>, sentinelClientId: string) {
    super();

    this.#validateOptions(options);

    this.#name = options.name;
    this.#sentinelClientId = sentinelClientId;

    this.#RESP = options.RESP;
    this.#keyPrefix = options.keyPrefix;
    this.#sentinelSeedNodes = Array.from(options.sentinelRootNodes);
    // Initial root nodes start as a copy of the seed nodes; transform() later
    // merges discovered nodes on top while preserving these seeds.
    this.#sentinelRootNodes = Array.from(this.#sentinelSeedNodes);
    this.#maxCommandRediscovers = options.maxCommandRediscovers ?? 16;

View on GitHub (pinned to 90fd0652bc)