{"id":"3295b573beb013fb","repo":"redis/node-redis","slug":"cannot-split-label-key-region-does-not-align-w","errorCode":null,"errorMessage":"Cannot split ${label}: key region does not align with keystep ${keyStep}","messagePattern":"Cannot split (.+?): key region does not align with keystep (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts","lineNumber":109,"sourceCode":"      if (numKeys === undefined) {\n        throw new Error(`Cannot split ${label}: malformed numkeys argument '${args[keyNumIdx]}'`);\n      }\n      keyRegionEnd = keyRegionStart + numKeys * keyStep;\n      break;\n    }\n    default:\n      throw new Error(`Cannot split ${label}: unsupported find_keys type '${findKeys.type}'`);\n  }\n\n  if (keyStep < 1) {\n    throw new Error(`Cannot split ${label}: invalid keystep ${keyStep}`);\n  }\n  if (keyRegionStart < 1 || keyRegionEnd > args.length) {\n    throw new Error(`Cannot split ${label}: key region overruns the arguments`);\n  }\n  const regionLength = keyRegionEnd - keyRegionStart;\n  if (regionLength <= 0 || regionLength % keyStep !== 0) {\n    throw new Error(`Cannot split ${label}: key region does not align with keystep ${keyStep}`);\n  }\n\n  const groupCount = regionLength / keyStep;\n  const slotGroups = new Map<number, Array<number>>();\n  for (let group = 0; group < groupCount; group++) {\n    const slot = calculateSlot(args[keyRegionStart + group * keyStep]);\n    const groups = slotGroups.get(slot);\n    if (groups) {\n      groups.push(group);\n    } else {\n      slotGroups.set(slot, [group]);\n    }\n  }\n\n  const subCommands = new Map<number, SubCommand>();\n\n  // Single-slot fast path: nothing to split — pass the original command\n  // through untouched (also preserves single-slot atomicity). Keys keep their","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts#L91-L127","documentation":"Thrown by splitMultiShardCommand when the cluster client tries to fan out a multi-shard command (DEL, UNLINK, EXISTS, TOUCH, MGET, MSET) but the argument region containing key/value groups is not a whole multiple of the command's keyStep. The splitter refuses to guess because a wrong split of a write command silently corrupts data, so this is a hard invariant violation in the command's declared key specification versus the actual argument array.","triggerScenarios":"Reached only on the internal routing path of cluster multi_shard commands. Triggered when (keyRegionEnd - keyRegionStart) % keyStep !== 0, e.g. MSET invoked with an odd number of trailing args after the command name and key region start, or a command whose COMMAND key spec (beginSearch.index / findKeys.keyStep) disagrees with the real arity of the args array passed in.","commonSituations":"Almost always indicates a bug in the command metadata (keySpecs) shipped with the client, or an arguments array that was mutated/truncated after command parsing. Can surface after upgrading the client or Redis server if a command's COMMAND DOCS key specification changed. Not something end users construct directly via the public API.","solutions":["Upgrade @redis/client to the latest patch release — this is an internal invariant mismatch, likely already fixed against the server's current COMMAND reply.","Capture the exact command name and argument array (args[0]) from the error label and file a bug against node-redis with the Redis server version and command involved.","If you trigger it via a custom command/sendCommand path, verify the argument count matches the command's documented arity before sending.","As a workaround, disable multi_shard routing for that command or issue the operation as separate single-key commands."],"exampleFix":"// before: relies on multi_shard split of a command whose metadata is wrong\nawait cluster.mset(['k1', 'v1', 'k2']); // odd trailing arg count after key region\n\n// after: pass complete key/value groups so the region aligns with keyStep 2\nawait cluster.mset(['k1', 'v1', 'k2', 'v2']);","handlingStrategy":"try-catch","validationCode":"// No caller-side validation possible: the mismatch is between command metadata\n// and the args array produced internally. Validate arity before sending if you\n// build args manually:\nfunction assertEvenKvPairs(args) {\n  if (args.length < 2 || (args.length % 2) !== 0) throw new TypeError('expected even key/value count');\n}","typeGuard":"// Guards that a flat array is complete key/value pairs for keyStep-2 commands.\nfunction isAlignedKvArray(arr) { return Array.isArray(arr) && arr.length > 0 && arr.length % 2 === 0; }","tryCatchPattern":"try { await cluster.mset(pairs); } catch (e) { if (/does not align with keystep/.test(e.message)) { /* metadata/arity bug — report, fall back to per-key SET */ for (const [k,v] of chunk(pairs,2)) await cluster.set(k,v); } else throw e; }","preventionTips":["Keep @redis/client updated so command metadata matches the server.","Build multi-key command args from structured key/value tuples, not hand-rolled arrays.","When using sendCommand with raw args, validate arity against COMMAND INFO before sending."],"tags":["cluster","multi-shard","internal-invariant","command-metadata"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}