{"id":"7a461a85f2979dde","repo":"redis/node-redis","slug":"unsupported-reply-type-for-merge-aggregation","errorCode":null,"errorMessage":"Unsupported reply type for merge aggregation","messagePattern":"Unsupported reply type for merge aggregation","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/generic-aggregators.ts","lineNumber":196,"sourceCode":"\t\tfor(const reply of replies) {\n\t\t\tfor(const [key, value] of reply as Map<unknown, unknown>) {\n\t\t\t\tmap.set(key, value);\n\t\t\t}\n\t\t}\n\t\treturn map as T;\n\t}\n\n\t// RESP3 map replies decode to plain objects under the default type\n\t// mapping; merge them like the Map branch (last node wins per key).\n\tif(typeof firstReply === 'object' && firstReply !== null) {\n\t\tconst merged: Record<string, unknown> = {};\n\t\tfor(const reply of replies) {\n\t\t\tObject.assign(merged, reply);\n\t\t}\n\t\treturn merged as T;\n\t}\n\n\tthrow new Error('Unsupported reply type for merge aggregation');\n\n};\n","sourceCodeStart":178,"sourceCodeEnd":199,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/generic-aggregators.ts#L178-L199","documentation":"The `agg_merge` reducer (default-keyless/special fallback for fan-out replies, e.g. KEYS under all_shards) merges replies only when the first reply is an Array, a Map, or a plain object. If the first reply is none of these (e.g. a scalar string, number, or null), it cannot merge and throws. Merge is last-node-wins per key for objects/Maps, union for arrays.","triggerScenarios":"A fan-out command whose response policy reduces via aggregateMerge (e.g. default-keyless fan-out like KEYS across all_shards) where the first node's reply is a scalar rather than a collection. With multiple shards returning scalars there is no defined merge.","commonSituations":"Issuing a command whose reply is scalar but whose policy fans out (custom override, or a special-policy command without a dedicated reducer falling back to merge); a node returning a scalar where a collection was expected.","solutions":["Check whether the command should really fan out — scalar-reply commands usually want default-keyed single-target routing.","Inspect the first node's reply shape directly to see why it is not a collection.","Provide a command-specific SPECIAL_RESPONSE_REDUCER if you are extending metadata for a fan-out scalar command."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isMergeable(reply: unknown): reply is Array<unknown> | Map<unknown, unknown> | Record<string, unknown> {\n  return Array.isArray(reply) || reply instanceof Map || (typeof reply === 'object' && reply !== null);\n}","tryCatchPattern":"try {\n  await cluster.sendCommand(['KEYS', '*']);\n} catch (e) {\n  if (/Unsupported reply type for merge/.test(e.message)) {\n    // first shard reply was a scalar — command may not be a real fan-out candidate\n  } else throw e;\n}","preventionTips":["Confirm a command should fan out before relying on merge reduction — scalar replies don't merge.","Provide a command-specific reducer if you extend metadata for a fan-out scalar command.","Inspect the first node's reply shape directly when merge fails."],"tags":["cluster","aggregation","response-policy","reply-shape","merge"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}