redis/node-redis · error · Error

All replies must be numbers for sum aggregation

Error message

All replies must be numbers for sum aggregation

What it means

The `agg_sum` reducer requires every shard reply to be a number before summing. If any reply is non-numeric (string, null, object), it refuses to aggregate. Used by multi_shard commands like DEL/UNLINK/EXISTS/TOUCH (count of affected keys summed across slots). The dispatcher strips NUMBER typeMapping for numeric aggregators, so the cause is a genuine shape mismatch, not a String-mapped number.

Source

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

    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;
};


export const aggregateMerge = <T>(replies: Array<unknown>): T => {
	if(replies.length === 0) return undefined as T;

	const firstReply = replies[0]

	if(Array.isArray(firstReply)) {
		const set = new Set()
		for(const reply of replies) {
			for(const item of reply as Array<unknown>) {
				set.add(item);
			}
		}
		return Array.from(set) as T;

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Run DEL/UNLINK/EXISTS/TOUCH with keys hashing to a single slot (hash tags) to isolate which shard misbehaves.
  2. Verify all nodes run the same Redis major/minor.
  3. Check cluster health — a failing node can return error replies that surface here.
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.del('{a}1', '{b}2', '{c}3');
} catch (e) {
  if (/must be numbers for sum/.test(e.message)) {
    // a shard returned a non-integer count — check node health/version
  } else throw e;
}

Prevention

When it happens

Trigger: A multi_shard command (DEL, UNLINK, EXISTS, TOUCH) or any agg_sum-tagged command where at least one shard's reply is not a number — e.g. a node returned an error reply that wasn't a rejection, or a version where the integer reply changed type.

Common situations: A shard returning an error string instead of an integer count; RESP version mismatch; a module replying with a different type on some node; running against an inhomogeneous cluster during a rolling upgrade.

Related errors


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