{"id":"9e168e803158da59","repo":"redis/node-redis","slug":"cannot-split-label-key-region-overruns-the-arg","errorCode":null,"errorMessage":"Cannot split ${label}: key region overruns the arguments","messagePattern":"Cannot split (.+?): key region overruns the arguments","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts","lineNumber":105,"sourceCode":"      if (keyNumIdx >= keyRegionStart) {\n        throw new Error(`Cannot split ${label}: numkeys argument inside the key region`);\n      }\n      const numKeys = parsePositiveInteger(args[keyNumIdx]);\n      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","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts#L87-L123","documentation":"The computed key region [keyRegionStart, keyRegionEnd) must lie within the actual args: keyRegionStart >= 1 (must skip the command name) and keyRegionEnd <= args.length. If the region overruns the args array — typically because a keynum spec's numkeys claims more key groups than were supplied — the splitter refuses rather than read past the end. For keynum specs this is a user-argument error (numkeys too large); for range specs the region is args.length-bounded by construction so this is harder to hit.","triggerScenarios":"Issuing a keynum-spec multi_shard command with numkeys larger than the supplied key groups (e.g. MSEDEX '3' with only one key/value pair), causing keyRegionEnd = firstKey + numkeys*keyStep to exceed args.length. For range specs, only reachable via a spec whose beginSearch.index is past the end of a very short arg list.","commonSituations":"A caller passing a numkeys count larger than the actual key groups on a keynum command; a truncated command buffer; a metadata spec with a wrong beginSearch.index.","solutions":["For keynum commands, pass a numkeys that exactly matches the number of key groups supplied.","Use the typed command API so the argument count is validated before routing.","Re-check metadata beginSearch.index if the spec itself is wrong."],"exampleFix":"// before — numkeys '3' but only one key/value group supplied\nawait cluster.sendCommand(['MSEDEX', '3', '{a}1', 'v1', 'NX', 'EX', '10']);\n// throws: Cannot split MSEDEX: key region overruns the arguments\n\n// after — numkeys matches the supplied groups\nawait cluster.sendCommand(['MSEDEX', '1', '{a}1', 'v1', 'NX', 'EX', '10']);","handlingStrategy":"validation","validationCode":"// For keynum-spec multi_shard commands, ensure numkeys matches the supplied groups.\nfunction buildKeynumCommand(name: string, groups: Array<[string, string]>, opts: string[]) {\n  const flat = groups.flat();\n  const numkeys = groups.length;\n  if (!Number.isInteger(numkeys) || numkeys < 1) throw new TypeError('at least one key group required');\n  return [name, String(numkeys), ...flat, ...opts];\n}\nconst args = buildKeynumCommand('MSEDEX', [['{a}1', 'v1']], ['NX', 'EX', '10']);\n// args.length will always exceed the computed keyRegionEnd\nawait cluster.sendCommand(args);","typeGuard":null,"tryCatchPattern":"try {\n  await cluster.sendCommand(['MSEDEX', numkeys, ...keyGroups, 'NX', 'EX', '10']);\n} catch (e) {\n  if (/key region overruns/.test(e.message)) {\n    // numkeys larger than supplied groups — fix the count\n  } else throw e;\n}","preventionTips":["For keynum commands, pass a numkeys that exactly matches the number of key groups supplied.","Derive numkeys from the key array length rather than hardcoding a count.","Prefer the typed command API so argument counts are validated before routing."],"tags":["cluster","multi-shard","keynum","user-input","splitter"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}