You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Cluster slot for String keys ignores byte-level hash tag under a DBCS default charsetΒ #4685
JedisClusterCRC16.getSlot(String) locates the {hash tag} by scanning for the { and }characters (through JedisClusterHashTag.getHashTag) and only encodes to bytes afterwards. The byte[] overload and the server's keyHashSlot() scan for 0x7B/0x7D in the encoded bytes.
Under a DBCS SafeEncoder.DEFAULT_CHARSET (GBK, Shift_JIS, Big5) a trail byte can be 0x7B/0x7D while the corresponding char is not a brace. The String path then picks a different tag (or none) than the wire bytes and computes a slot the server does not own. The same logical key also routes to two different slots depending on whether the String or the binary API is used.
getSlot(String) backs CommandArguments.getKeyHashSlots(), ClusterCommandObjects key routing and sharded pub/sub, so this affects routing for every keyed command, not just SCAN.
Minimal reproducible example
SafeEncoder.DEFAULT_CHARSET = Charset.forName("GBK");
// GBK bytes: 81 7B 41 81 7D -> a lead byte + '{' , 'A', a lead byte + '}'Stringkey = newString(newbyte[]{(byte)0x81,0x7B,0x41,(byte)0x81,0x7D}, SafeEncoder.DEFAULT_CHARSET);
JedisClusterCRC16.getSlot(key); // 14821 (whole key hashed, no '{' char)JedisClusterCRC16.getSlot(SafeEncoder.encode(key)); // 16212 (== server keyHashSlot)
Single-key ops survive only via a MOVED round-trip.
Multi-key commands are mis-grouped: keys the server co-locates are rejected client-side (Cannot get connection for command with multiple hash slots), and keys the client groups get a server-side CROSSSLOT.
Under UTF-8 there is no discrepancy (no multibyte sequence contains 0x7B/0x7D), so a fix is a no-op for the default configuration.
Practical benefit
Non-UTF-8 default charsets are a supported knob (SafeEncoder.DEFAULT_CHARSET, used for GBK/Shift_JIS/Big5 deployments). Making getSlot(String) scan the encoded bytes brings it in line with the binary overload and the server, so hash tags co-locate correctly on those deployments.
Proposed fix
Have getSlot(String) delegate to the byte[] overload so brace scanning happens on the wire bytes: return getSlot(SafeEncoder.encode(key));.
Before we go further, could you share some additional context:
How did you find this issue β code review, or did it surface in a real deployment?
What charset is set as DEFAULT_CHARSET in your environment?
How does it manifest: MOVED redirects, a CROSSSLOT error, or silent wrong-data routing?
A minimal reproducer against an actual Redis Cluster deployment β showing the observable failure β would help us assess real-world impact and validate the fix end-to-end.
Under a DBCS SafeEncoder.DEFAULT_CHARSET (GBK, Shift_JIS, Big5) a trail byte can be 0x7B/0x7D while the char is not a brace, so the String path picks a different tag (or none) and routes to a slot the server does not own.
The fix makes Jedis consistent with the server in a configuration that produces incorrect behavior regardless. Jedis agreeing with the server on slot 16212 for ηΈ»AηΈ½ doesn't help the user β their key is still being hash-tag-routed on a byte they never intended as a delimiter.
Seems DBCS encodings are incompatible with Redis Cluster hash tag semantics at the protocol level. The server scans raw bytes for 0x7B/0x7D with no charset awareness, and DBCS trail bytes can collide with those values in ways the client cannot prevent.
One solution is to use UTF-8 (the default) with Redis Cluster, or use the byte[] API directly.
If you believe there is a concrete use case not covered here, please reopen with a minimal reproducer showing the exact deployment, the observed failure, and the expected behavior from the user's perspective.
After taking a second look, unifying getSlot(String) with getSlot(byte[]) is worth doing on its own, separate from whether DBCS hash tags are advisable.
Scope: this does not make arbitrary DBCS hash tags safe β a trail byte can still form an unintended tag, so the guidance to anchor tags in ASCII (and prefer UTF-8 / the byte[] API for DBCS content) stands. It only guarantees String and byte[] routing agree.
Today the two overloads return different slots for the same logical key under a DBCS default charset β e.g. GBK: getSlot(key)=14821 vs getSlot(SafeEncoder.encode(key))=16212
Activity
ggivo commentedon Aug 17, 2026
Thanks for reporting it.
We will triage it as soon as we have free slots.
ggivo commentedon Aug 17, 2026
Before we go further, could you share some additional context:
DEFAULT_CHARSETin your environment?CROSSSLOTerror, or silent wrong-data routing?A minimal reproducer against an actual Redis Cluster deployment β showing the observable failure β would help us assess real-world impact and validate the fix end-to-end.
ggivo commentedon Aug 17, 2026
The fix makes Jedis consistent with the server in a configuration that produces incorrect behavior regardless. Jedis agreeing with the server on slot 16212 for ηΈ»AηΈ½ doesn't help the user β their key is still being hash-tag-routed on a byte they never intended as a delimiter.
Seems DBCS encodings are incompatible with Redis Cluster hash tag semantics at the protocol level. The server scans raw bytes for 0x7B/0x7D with no charset awareness, and DBCS trail bytes can collide with those values in ways the client cannot prevent.
One solution is to use UTF-8 (the default) with Redis Cluster, or use the byte[] API directly.
If you believe there is a concrete use case not covered here, please reopen with a minimal reproducer showing the exact deployment, the observed failure, and the expected behavior from the user's perspective.
ggivo commentedon Sep 21, 2026
After taking a second look, unifying
getSlot(String)withgetSlot(byte[])is worth doing on its own, separate from whether DBCS hash tags are advisable.Scope: this does not make arbitrary DBCS hash tags safe β a trail byte can still form an unintended tag, so the guidance to anchor tags in ASCII (and prefer UTF-8 / the
byte[]API for DBCS content) stands. It only guaranteesStringandbyte[]routing agree.Today the two overloads return different slots for the same logical key under a DBCS default charset β e.g. GBK:
getSlot(key)=14821 vsgetSlot(SafeEncoder.encode(key))=16212Fix cluster slot mismatch between String and byte[] keys under non-UTβ¦