{"id":"a7c90e286e415e7c","repo":"redis/node-redis","slug":"all-replies-must-be-number-arrays-of-equal-length","errorCode":null,"errorMessage":"All replies must be number arrays of equal length for ${label} aggregation","messagePattern":"All replies must be number arrays of equal length for (.+?) aggregation","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/generic-aggregators.ts","lineNumber":93,"sourceCode":" * Shared by the array path of `aggregateMin`/`aggregateMax` so element-wise\n * aggregation matches the server's AGG_MIN/AGG_MAX semantics for commands\n * whose reply is an array (e.g. WAITAOF's `[numlocal, numreplicas]`).\n */\nconst aggregateElementwise = (\n  replies: Array<unknown>,\n  reduce: (a: number, b: number) => number,\n  label: string\n): Array<number> => {\n  const length = (replies[0] as Array<unknown>).length;\n  if (\n    !replies.every(\n      (reply): reply is number[] =>\n        Array.isArray(reply) &&\n        reply.length === length &&\n        reply.every((value): value is number => typeof value === 'number')\n    )\n  ) {\n    throw new Error(\n      `All replies must be number arrays of equal length for ${label} aggregation`\n    );\n  }\n\n  const result = (replies[0] as number[]).slice();\n  for (let r = 1; r < replies.length; r++) {\n    const reply = replies[r] as number[];\n    for (let i = 0; i < length; i++) {\n      result[i] = reduce(result[i], reply[i]);\n    }\n  }\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","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/generic-aggregators.ts#L75-L111","documentation":"The element-wise helper used by `agg_min`/`agg_max` (array path, e.g. WAITAOF's [numlocal, numreplicas]) requires every shard reply to be a number array of the SAME length as the first reply before folding column-wise. Differing lengths or non-number elements abort the fold — element-wise min/max is undefined when columns don't line up.","triggerScenarios":"A command tagged `agg_min`/`agg_max` whose first shard reply is an array, but some other shard returns an array of different length or with non-number elements. WAITAOF is the canonical example; any future command returning per-shard numeric arrays hits this if shapes diverge.","commonSituations":"Version skew where WAITAOF returns a 2-element array on some nodes and a different shape on others; a node error transformed into a non-array; mixed RESP versions producing different array shapes.","solutions":["Ensure all nodes run a Redis version with a consistent reply shape for the aggregated command.","Run the command against each master directly to compare array lengths.","Check for custom typeMapping or response-policy overrides that alter the reply shape."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isUniformNumberArrays(replies: unknown[]): replies is number[][] {\n  return replies.every(r =>\n    Array.isArray(r) && r.every(v => typeof v === 'number') && r.length === (replies[0] as number[])?.length\n  );\n}","tryCatchPattern":"try {\n  await cluster.sendCommand(['WAITAOF', '1', '1', '0']);\n} catch (e) {\n  if (/number arrays of equal length/.test(e.message)) {\n    // shard reply shapes diverge — check version parity\n  } else throw e;\n}","preventionTips":["Ensure all nodes run a Redis version with a consistent reply shape for the aggregated command.","Compare per-master replies directly when shapes diverge.","Avoid custom typeMapping that alters array element types for these commands."],"tags":["cluster","aggregation","response-policy","reply-shape","waitaof"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}