{"record":{"id":"e6eda177c4bf6c65","repo":"redis/jedis","slug":"cluster-mode-only-supports-scan-command-with-match","errorCode":null,"errorMessage":"Cluster mode only supports SCAN command with MATCH pattern containing hash-tag ( curly-brackets enclosed string )","messagePattern":"Cluster mode only supports SCAN command with MATCH pattern containing hash-tag \\( curly-brackets enclosed string \\)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/ClusterCommandObjects.java","lineNumber":34,"sourceCode":"import java.util.stream.Collectors;\n\nimport static redis.clients.jedis.Protocol.Command.*;\nimport static redis.clients.jedis.Protocol.Keyword.TYPE;\n\npublic class ClusterCommandObjects extends CommandObjects {\n\n  private static final String CLUSTER_UNSUPPORTED_MESSAGE = \"Not supported in cluster mode.\";\n\n  public ClusterCommandObjects(RedisProtocol protocol) {\n    super(protocol);\n  }\n\n  private static final String SCAN_PATTERN_MESSAGE = \"Cluster mode only supports SCAN command\"\n      + \" with MATCH pattern containing hash-tag ( curly-brackets enclosed string )\";\n\n  @Override\n  public final CommandObject<ScanResult<String>> scan(String cursor) {\n    throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE);\n  }\n\n  @Override\n  public final CommandObject<ScanResult<String>> scan(String cursor, ScanParams params) {\n    String match = params.match();\n    if (match == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(match)) {\n      throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE);\n    }\n    return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).addHashSlotKey(match), BuilderFactory.SCAN_RESPONSE);\n  }\n\n  @Override\n  public final CommandObject<ScanResult<String>> scan(String cursor, ScanParams params, String type) {\n    String match = params.match();\n    if (match == null || !JedisClusterHashTag.isClusterCompliantMatchPattern(match)) {\n      throw new IllegalArgumentException(SCAN_PATTERN_MESSAGE);\n    }\n    return new CommandObject<>(commandArguments(SCAN).add(cursor).addParams(params).addHashSlotKey(match).add(TYPE).add(type), BuilderFactory.SCAN_RESPONSE);","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/ClusterCommandObjects.java#L16-L52","documentation":"In cluster mode, SCAN without a MATCH pattern cannot be routed to a hash slot, so ClusterCommandObjects.scan(String cursor) unconditionally throws IllegalArgumentException. Redis Cluster requires key-space operations to target a slot, and a MATCH pattern containing a hash-tag ({...}) provides that slot.","triggerScenarios":"Calling `clusterClient.scan(cursor)` (or JedisCluster scan variants) on a RedisClusterClient/ClusterCommandObjects with only a cursor and no MATCH-containing-hash-tag params.","commonSituations":"Porting standalone-mode scanning code to Redis Cluster; iterating all keys in a cluster the same way as a single-node Redis.","solutions":["Use `scan(cursor, new ScanParams().match(\"{prefix}.*\"))` with a hash-tag enclosed in curly brackets","Scan each master node individually with dedicated connections if a cluster-wide key listing is required","Use a non-cluster client for single-node setups where untagged SCAN is valid"],"exampleFix":"// before\nScanResult<String> r = cluster.scan(cursor);\n// after\nScanResult<String> r = cluster.scan(cursor, new ScanParams().match(\"{user:1}:*\"));","handlingStrategy":"validation","validationCode":"if (JedisClusterHashTag.isClusterCompliantMatchPattern(pattern)) { result = cluster.scan(cursor, new ScanParams().match(pattern)); } else { /* scan per node or fix pattern */ }","typeGuard":"boolean isClusterSafePattern(String p) { return p != null && p.contains(\"{\") && p.contains(\"}\"); }","tryCatchPattern":"try { cluster.scan(cursor); } catch (IllegalArgumentException e) { throw new IllegalStateException(\"Cluster SCAN requires a hash-tagged MATCH pattern; use scan(cursor, params)\", e); }","preventionTips":["Never use cursor-only SCAN against a cluster client","Design keys with a common hash-tag prefix so scans can target one slot","For full keyspace iteration, iterate each master node with its own connection"],"tags":["redis-cluster","scan","hash-tag","illegal-argument"],"backgroundTag":"unsupported-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"}