{"id":"432eeaa1afbef427","repo":"redis/node-redis","slug":"all-replies-must-be-numbers-for-max-aggregation","errorCode":null,"errorMessage":"All replies must be numbers for max aggregation","messagePattern":"All replies must be numbers for max aggregation","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/generic-aggregators.ts","lineNumber":140,"sourceCode":"    throw new Error('All replies must be numbers for min aggregation');\n  }\n  return Math.min(...replies) as T;\n};\n\n/**\n * Aggregates shard replies by taking the maximum value.\n * @remarks\n * Mirrors {@link aggregateMin}: scalar replies fold to a single maximum, array\n * replies fold element-wise (AGG_MAX semantics). Input structure is validated\n * at runtime; the generic `T` is for call-site ergonomy only.\n */\nexport const aggregateMax = <T>(replies: Array<unknown>): T => {\n  if (replies.length === 0) return 0 as T;\n  if (Array.isArray(replies[0])) {\n    return aggregateElementwise(replies, Math.max, 'max') as T;\n  }\n  if (!replies.every((reply): reply is number => typeof reply === 'number')) {\n    throw new Error('All replies must be numbers for max aggregation');\n  }\n  return Math.max(...replies) as T;\n};\n\n/**\n * Aggregates multiple numbers by finding the sum of all values.\n * @remarks\n * This implementation is specifically designed for Array<number> type only,\n * despite the generic type parameter. The generic type parameter T is provided\n * for usage ergonomy, but the actual input structure will be validated at runtime.\n */\nexport const aggregateSum = <T>(replies: Array<unknown>): T => {\n  if (replies.length === 0) return 0 as T;\n  if (!replies.every((reply): reply is number => typeof reply === 'number')) {\n    throw new Error('All replies must be numbers for sum aggregation');\n  }\n  return replies.reduce((acc, reply) => acc + reply, 0) as T;\n};","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/generic-aggregators.ts#L122-L158","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Identify the offending node by running the command against each master directly.","Verify Redis version parity.","Audit custom typeMapping entries affecting the command's RESP types."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function allNumbers(replies: unknown[]): replies is number[] {\n  return replies.every(r => typeof r === 'number');\n}","tryCatchPattern":"try {\n  await cluster.sendCommand(cmd, ...args);\n} catch (e) {\n  if (/must be numbers for max/.test(e.message)) {\n    // a shard returned a non-number — isolate the node\n  } else throw e;\n}","preventionTips":["Verify Redis version consistency across the cluster.","Isolate the offending node by running the command per-master.","Audit custom typeMapping entries affecting the command's RESP types."],"tags":["cluster","aggregation","response-policy","reply-shape"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}