{"record":{"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/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/cluster/request-response-policies/generic-aggregators.ts#L178-L199","documentation":"Thrown by aggregateMerge when the first shard's reply is not an array, Map, or plain object. This reducer is used by the default_keyless response policy for fan-out commands (e.g. KEYS under all_shards) and by the special-policy fallback. It merges arrays (union via Set), Maps (last-node-wins per key), and plain objects (Object.assign). Any other reply type (string, number, boolean, null) cannot be merged.","triggerScenarios":"A fan-out command under default_keyless or special response policy where shards return scalar replies — e.g. a command that returns a string or number on each node being routed all_shards with default_keyless merging. The special-policy fallback also hits this when a command has no dedicated reducer and its shard replies are scalars.","commonSituations":"Calling a command with all_shards routing that returns scalar replies per node; a command tagged 'special' response with no registered per-command reducer whose replies are not collection types.","solutions":["Identify the command triggering the merge and verify its reply shape is a collection (array, Map, or object) under fan-out","If the command returns scalars, use a different approach (e.g. route to a single node, or use sendCommand with a custom aggregation)","For special-policy commands, check whether a dedicated reducer should be registered"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isMergeAggError(err: unknown): boolean {\n  return err instanceof Error && err.message.includes('merge aggregation');\n}","tryCatchPattern":"try {\n  await cluster.sendCommand(true, 'KEYS', '*');\n} catch (err) {\n  if (err instanceof Error && err.message.includes('merge aggregation')) {\n    // fan-out reply was not a collection type — route to single node instead\n    const nodes = cluster.nodes('master');\n    const results = await Promise.all(nodes.map(n => n.keys('*')));\n  } else {\n    throw err;\n  }\n}","preventionTips":["Verify that commands routed all_shards/default_keyless return collection-type replies (arrays, Maps, objects)","For commands returning scalars, route to individual nodes and aggregate manually","Check whether a special-policy command should have a dedicated per-command reducer registered"],"tags":["cluster","aggregation","merge","response-validation"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}