redis/node-redis · error · Error

All replies must be numbers for min aggregation

Error message

All replies must be numbers for min aggregation

What it means

The `agg_min` reducer's scalar path: the first shard reply was NOT an array, so the reducer expects all replies to be plain numbers to compute Math.min. If any reply is not a number, it refuses to aggregate. The dispatcher strips the caller's NUMBER typeMapping for numeric aggregators, so a NUMBER:String mapping does not cause this — the cause is a genuine non-numeric reply (e.g. WAIT returning a non-integer, or an error reply shape).

Source

Thrown at packages/client/lib/cluster/request-response-policies/generic-aggregators.ts:122

  }
  return result;
};

/**
 * Aggregates shard replies by taking the minimum value.
 * @remarks
 * Scalar replies (e.g. WAIT, the minimal number of synchronized replicas) fold
 * to a single minimum; array replies (e.g. WAITAOF's `[numlocal, numreplicas]`)
 * fold element-wise, matching the server's AGG_MIN semantics. Input structure
 * is validated at runtime; the generic `T` is for call-site ergonomy only.
 */
export const aggregateMin = <T>(replies: Array<unknown>): T => {
  if (replies.length === 0) return 0 as T;
  if (Array.isArray(replies[0])) {
    return aggregateElementwise(replies, Math.min, 'min') as T;
  }
  if (!replies.every((reply): reply is number => typeof reply === 'number')) {
    throw new Error('All replies must be numbers for min aggregation');
  }
  return Math.min(...replies) as T;
};

/**
 * Aggregates shard replies by taking the maximum value.
 * @remarks
 * Mirrors {@link aggregateMin}: scalar replies fold to a single maximum, array
 * replies fold element-wise (AGG_MAX semantics). Input structure is validated
 * at runtime; the generic `T` is for call-site ergonomy only.
 */
export const aggregateMax = <T>(replies: Array<unknown>): T => {
  if (replies.length === 0) return 0 as T;
  if (Array.isArray(replies[0])) {
    return aggregateElementwise(replies, Math.max, 'max') as T;
  }
  if (!replies.every((reply): reply is number => typeof reply === 'number')) {
    throw new Error('All replies must be numbers for max aggregation');

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Run the command against each master directly to find which node returned a non-number.
  2. Confirm Redis version consistency across the cluster.
  3. Review any custom typeMapping entries that might transform the relevant RESP type.
Defensive patterns

Strategy: try-catch

Type guard

function allNumbers(replies: unknown[]): replies is number[] {
  return replies.every(r => typeof r === 'number');
}

Try / catch

try {
  await cluster.sendCommand(['WAIT', '1', '0']);
} catch (e) {
  if (/must be numbers for min/.test(e.message)) {
    // a shard returned a non-number — isolate the node
  } else throw e;
}

Prevention

When it happens

Trigger: A command tagged `agg_min` where replies are scalars (e.g. WAIT) but at least one shard returned a non-number. The element-wise array path is skipped because replies[0] is not an array; then the every(typeof === 'number') check fails.

Common situations: A WAIT reply that came back as null or a string due to an error; version skew changing WAIT's reply type; a custom typeMapping converting the number to a non-number for a non-NUMBER RESP type that the strip did not cover.

Related errors


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