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

Thrown by aggregateMax (scalar path) when at least one shard's reply is not a number. Used by CLIENT DISCARD / CLIENT DISCARDALL (all_shards + agg_max), where each shard returns a scalar integer. Mirrors aggregateMin's validation: if the first reply is not an array, all replies must be scalar numbers.

Solutions

  1. Ensure all cluster nodes run the same Redis version
  2. Check shard health and connectivity
  3. Verify typeMapping configuration does not interfere with NUMBER decoding
Defensive patterns

Strategy: try-catch

Type guard

function isMaxAggError(err: unknown): boolean {
  return err instanceof Error && err.message.includes('max aggregation');
}

Try / catch

try {
  // e.g. CLIENT DISCARD under cluster mode
  await cluster.sendCommand(true, 'CLIENT', 'DISCARD');
} catch (err) {
  if (err instanceof Error && err.message.includes('max aggregation')) {
    // shard returned non-number — check version consistency
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: CLIENT DISCARD/DISCARDALL under cluster mode where one shard returns a non-number; a shard erroring or returning an unexpected reply type; RESP decode producing a non-number.

Common situations: Mixed Redis versions; degraded shard returning an unexpected reply; type-mapping interference.

Related errors


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

Appendix: 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 90fd0652bc)