{"record":{"id":"5d1891e1e6973aea","repo":"redis/jedis","slug":"cannot-get-nodekey-for-command-with-multiple-hash","errorCode":null,"errorMessage":"Cannot get NodeKey for command with multiple hash slots","messagePattern":"Cannot get NodeKey for command with multiple hash slots","errorType":"exception","errorClass":"JedisClusterOperationException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/ClusterPipeline.java","lineNumber":144,"sourceCode":"      return new ClusterCommandObjects(RedisProtocol.orServerDefault(effective));\n    };\n  }\n\n  @Override\n  public void close() {\n    try {\n      super.close();\n    } finally {\n      IOUtils.closeQuietly(closeable);\n    }\n  }\n\n  @Override\n  protected HostAndPort getNodeKey(CommandArguments args) {\n    Set<Integer> slots = args.getKeyHashSlots();\n\n    if (slots.size() > 1) {\n      throw new JedisClusterOperationException(\"Cannot get NodeKey for command with multiple hash slots\");\n    }\n\n    if (slots.isEmpty()) {\n      return null; // Let getConnection(null) handle it by using a random node\n    }\n\n    return provider.getNode(slots.iterator().next());\n  }\n\n  @Override\n  protected Connection getConnection(HostAndPort nodeKey) {\n    return provider.getConnection(nodeKey);\n  }\n\n  public Response<Long> spublish(String channel, String message) {\n    return appendCommand(commandObjects.spublish(channel, message));\n  }\n","sourceCodeStart":126,"sourceCodeEnd":162,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/ClusterPipeline.java#L126-L162","documentation":"ClusterPipeline.getNodeKey determines which cluster node a pipelined command should be sent to by looking at the command's hash slots. Multi-key commands whose keys map to more than one hash slot cannot be assigned to a single node, so JedisClusterOperationException is thrown. Such commands must be executed with hash tags or via cross-slot-safe APIs.","triggerScenarios":"Pipelining (ClusterPipeline/PipeliningBase) multi-key commands like MSET, DEL k1 k2, SUNION, or RENAME whose keys hash to different slots — i.e. keys without matching {hashtag} prefixes.","commonSituations":"Bulk-deleting or bulk-writing many unrelated keys in one pipeline call; renaming or moving data between keys created without hash tags; moving from standalone (where multi-key ops are free) to cluster mode.","solutions":["Wrap related keys in the same hash tag so they share a slot, e.g. {user42}.profile and {user42}.settings.","Split the multi-key command into per-key commands so each pipelined command targets a single slot.","Execute the multi-key command outside the pipeline (cluster-level APIs may reshard/route differently), or group commands by slot before pipelining."],"exampleFix":"// before\npipeline.mset(\"user:1:name\", \"a\", \"user:2:name\", \"b\");\n// after\npipeline.mset(\"{user:1}:name\", \"a\", \"{user:1}:email\", \"x\"); // keys share slot","handlingStrategy":"validation","validationCode":"static void assertSameSlot(String... keys) {\n  Set<Integer> slots = Arrays.stream(keys)\n      .map(k -> JedisClusterCRC16.getSlot(JedisClusterHashTagUtil.getHashTag(k) != null ? k : k))\n      .collect(Collectors.toSet());\n  if (slots.size() > 1)\n    throw new IllegalStateException(\"Keys span multiple slots: use hash tags\");\n}","typeGuard":"static boolean sameSlot(String a, String b) {\n  return JedisClusterCRC16.getSlot(a) == JedisClusterCRC16.getSlot(b);\n}","tryCatchPattern":"try {\n  pipeline.mset(pairs);\n} catch (JedisClusterOperationException e) {\n  if (e.getMessage().contains(\"multiple hash slots\")) {\n    // fall back to per-key commands or wrap keys in a hash tag\n  } else throw e;\n}","preventionTips":["Design key names with hash tags ({userId}:field) for data accessed together.","Batch multi-key operations per slot instead of globally.","Verify multi-key commands with JedisClusterCRC16.getSlot() in unit tests."],"tags":["redis-cluster","cross-slot","pipeline"],"backgroundTag":"cross-slot-operation","analyzedSha":"6dac31d4c224fb3257c216f3985340c6f500cdcb","analyzedAt":"2026-09-08T04:55:01.204Z","contentChangedAt":"2026-09-08T04:55:01.204Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}