{"id":"271d686230926548","repo":"redis/node-redis","slug":"cannot-split-label-command-has-no-key-specific","errorCode":null,"errorMessage":"Cannot split ${label}: command has no key specification","messagePattern":"Cannot split (.+?): command has no key specification","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts","lineNumber":46,"sourceCode":" * relative order) + suffix; for `keynum` specs the numkeys argument in the\n * prefix is rewritten to the sub-command's group count.\n *\n * Throws on anything it cannot split deterministically — a wrong split of a\n * write command means corrupted data, so refusal beats guessing. All current\n * multi_shard commands (DEL, UNLINK, EXISTS, TOUCH, MGET, MSET) declare\n * exactly one supported spec. MSETEX is curated OUT of multi_shard\n * (command-metadata-overrides.ts): its NX/XX condition is all-or-nothing\n * across all keys and cannot be evaluated per shard — it routes default-keyed\n * like MSETNX, so the keynum branch below currently has no live caller.\n */\nexport function splitMultiShardCommand(\n  args: ReadonlyArray<RedisArgument>,\n  keySpecs: ReadonlyArray<KeySpec> | undefined\n): Map<number, SubCommand> {\n  const label = args.length > 0 ? args[0].toString() : '<empty>';\n\n  if (!keySpecs || keySpecs.length === 0) {\n    throw new Error(`Cannot split ${label}: command has no key specification`);\n  }\n  // TODO(multi-spec): a command whose keys are interchangeable but\n  // syntactically scattered (e.g. a fixed-position key plus a keyword-tail\n  // list) could legitimately be multi_shard with several specs, and\n  // multi-region reconstruction would be deterministic. No such command\n  // exists, and specs alone cannot distinguish that shape from linked-operand\n  // specs (GEORADIUS-like) where splitting is meaningless — so refuse until a\n  // real command motivates multi-region support.\n  if (keySpecs.length > 1) {\n    throw new Error(`Cannot split ${label}: multiple key specifications are not supported`);\n  }\n\n  const { beginSearch, findKeys } = keySpecs[0];\n  if (beginSearch.type !== 'index') {\n    throw new Error(`Cannot split ${label}: unsupported begin_search type '${beginSearch.type}'`);\n  }\n\n  const start = beginSearch.index;","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/request-response-policies/multi-shard-splitter.ts#L28-L64","documentation":"splitMultiShardCommand refuses to split a multi_shard command whose COMMAND key specification is missing or empty. Without a key spec the splitter cannot locate the key region to partition per slot, and a wrong split of a write command would corrupt data — so it throws deterministically. All current multi_shard commands (DEL, UNLINK, EXISTS, TOUCH, MGET, MSET) declare exactly one spec; hitting this means the metadata is malformed for a multi_shard-tagged command.","triggerScenarios":"A command tagged `multi_shard` in the metadata whose keySpecs array is undefined/empty. Reachable after a metadata regeneration that tags a new command multi_shard but fails to capture its key specs, or a manual metadata override that drops keySpecs.","commonSituations":"Custom command-metadata override assigning multi_shard without keySpecs; a metadata regeneration bug against a Redis version that reports no key specs for the tagged command.","solutions":["Check command-metadata overrides for the offending command — multi_shard requires a populated keySpecs.","Re-run metadata generation against a Redis version that reports key specs for the command.","If the command genuinely has no key spec, it is not multi_shard — change its request policy to default-keyed or default-keyless."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function hasKeySpec(specs: unknown): specs is Array<{ beginSearch: object; findKeys: object }> {\n  return Array.isArray(specs) && specs.length > 0;\n}","tryCatchPattern":"try {\n  await cluster.del('k1', 'k2');\n} catch (e) {\n  if (/no key specification/.test(e.message)) {\n    // metadata for this multi_shard command is malformed — regenerate or override\n  } else throw e;\n}","preventionTips":["After regenerating command metadata, verify multi_shard commands carry populated keySpecs.","Audit command-metadata overrides before assigning multi_shard.","If a command has no key spec, route it default-keyed/default-keyless instead."],"tags":["cluster","multi-shard","metadata","keyspec","splitter"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}