redis/node-redis · error · Error

Unknown response policy ${responsePolicy}

Error message

Unknown response policy ${responsePolicy}

What it means

Thrown from RedisCluster._executeWithPolicies (cluster/index.ts:577) when the resolved response policy string has no entry in RESPONSE_REDUCERS. The reducer table covers one_succeeded, all_succeeded, agg_logical_and/or/min/max/sum, special, default-keyless/keyed; an unknown response policy cannot be aggregated and the command is aborted before any node is contacted (to avoid orphaned in-flight replies).

Source

Thrown at packages/client/lib/cluster/index.ts:577

    // none of them default-keyed).
    if (
      requestPolicy === REQUEST_POLICIES_WITH_DEFAULTS.DEFAULT_KEYED &&
      responsePolicy === RESPONSE_POLICIES_WITH_DEFAULTS.DEFAULT_KEYED
    ) {
      return this._execute(parser, readonly, options, makeFn(parser));
    }

    // https://redis.io/docs/latest/develop/reference/command-tips
    const router = REQUEST_ROUTERS[requestPolicy];
    if (!router) {
      throw new Error(`Unknown request policy ${requestPolicy}`);
    }
    // Validated before any per-node promise is dispatched — throwing after
    // would orphan in-flight rejections (unhandled-rejection noise) and run
    // side effects for a reply that can never be reduced.
    const reducer = RESPONSE_REDUCERS[responsePolicy];
    if (!reducer) {
      throw new Error(`Unknown response policy ${responsePolicy}`);
    }
    // Routers are typed against the erased base cluster types (routing is
    // below the typed command surface); bridge this instantiation's slots in.
    const plan = await router(
      this._slots as unknown as Parameters<typeof router>[0],
      parser,
      readonly,
      policy.keySpecs
    );

    if (plan.length === 0) {
      throw new Error(`Request policy ${requestPolicy} produced no target nodes`);
    }

    // Numeric aggregation must see raw numbers: strip the caller's type
    // mapping from the per-node executions (a `NUMBER: String` mapping would
    // feed strings into the numeric reducers) and re-apply it to the
    // aggregated result below. Pass-through policies keep the mapping — their

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Upgrade the client to a version whose reducer table includes the policy.
  2. For custom commands, leave the response policy unset so the default applies.
  3. Report the interpolated policy string (in the error message) along with the command and server version.

Example fix

// no direct user API; mitigation is version alignment:
// before: module command resolves to response policy 'agg_avg' (unsupported)
// after: upgrade client, or invoke the command on a single node via getSlotMaster to bypass fan-out
const node = await cluster.getSlotMaster('k');
await node.sendCommand(['MYMODULECMD', 'k']);
Defensive patterns

Strategy: fallback

Type guard

function isUnknownResponsePolicy(e: unknown): boolean {
  return e instanceof Error && /Unknown response policy/i.test(e.message);
}

Try / catch

try { await cluster.sendCommand(cmd); }
catch (e) {
  if (isUnknownResponsePolicy(e)) {
    const node = await cluster.getSlotMaster(key);
    return node.sendCommand(cmd); // bypass fan-out aggregation
  }
  throw e;
}

Prevention

When it happens

Trigger: A command/module whose resolved response policy is a string outside the reducer table — e.g. a server-emitted response_policy tip this client version does not implement, or a malformed custom command registration.

Common situations: Server/client version skew introducing a new aggregation tip; custom commands with non-standard response policies; module commands returning unsupported tips.

Related errors


AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03). Data as JSON: /data/errors/695ccd8c022a029c.json. Report an issue: GitHub.