{"id":"71637bc49ad2064d","repo":"redis/node-redis","slug":"all-replies-must-be-numbers-for-min-aggregation","errorCode":null,"errorMessage":"All replies must be numbers for min aggregation","messagePattern":"All replies must be numbers for min aggregation","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/generic-aggregators.ts","lineNumber":122,"sourceCode":"  }\n  return result;\n};\n\n/**\n * Aggregates shard replies by taking the minimum value.\n * @remarks\n * Scalar replies (e.g. WAIT, the minimal number of synchronized replicas) fold\n * to a single minimum; array replies (e.g. WAITAOF's `[numlocal, numreplicas]`)\n * fold element-wise, matching the server's AGG_MIN semantics. Input structure\n * is validated at runtime; the generic `T` is for call-site ergonomy only.\n */\nexport const aggregateMin = <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.min, 'min') as T;\n  }\n  if (!replies.every((reply): reply is number => typeof reply === 'number')) {\n    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');","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/generic-aggregators.ts#L104-L140","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Run the command against each master directly to find which node returned a non-number.","Confirm Redis version consistency across the cluster.","Review any custom typeMapping entries that might transform the relevant RESP type."],"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(['WAIT', '1', '0']);\n} catch (e) {\n  if (/must be numbers for min/.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.","Run the command against each master directly to find the offending node.","Audit custom typeMapping entries affecting the command's RESP types."],"tags":["cluster","aggregation","response-policy","reply-shape","wait"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}