redis/node-redis · error · Error

All replies must be numbers for max aggregation

Error message

All replies must be numbers for max aggregation

What it means

Mirror of agg_min's scalar guard for `agg_max`. The first reply was not an array, so the reducer expects all replies to be plain numbers for Math.max; any non-number reply aborts. Same numeric-mapping strip applies, so the cause is a genuine non-numeric scalar from at least one shard.

Source

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

    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');
  }
  return Math.max(...replies) as T;
};

/**
 * Aggregates multiple numbers by finding the sum of all values.
 * @remarks
 * This implementation is specifically designed for Array<number> type only,
 * despite the generic type parameter. The generic type parameter T is provided
 * for usage ergonomy, but the actual input structure will be validated at runtime.
 */
export const aggregateSum = <T>(replies: Array<unknown>): T => {
  if (replies.length === 0) return 0 as T;
  if (!replies.every((reply): reply is number => typeof reply === 'number')) {
    throw new Error('All replies must be numbers for sum aggregation');
  }
  return replies.reduce((acc, reply) => acc + reply, 0) as T;
};

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Identify the offending node by running the command against each master directly.
  2. Verify Redis version parity.
  3. Audit custom typeMapping entries affecting the command's RESP types.
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(cmd, ...args);
} catch (e) {
  if (/must be numbers for max/.test(e.message)) {
    // a shard returned a non-number — isolate the node
  } else throw e;
}

Prevention

When it happens

Trigger: A command tagged `agg_max` whose replies are scalars but at least one shard returned a non-number value. As with agg_min, the array path is skipped and the scalar type check fails.

Common situations: Version skew; an error reply leaking through as a non-number; a transformed reply from a custom typeMapping not covered by the numeric-aggregator strip.

Related errors


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