Skip to content

Locale-dependent case conversion breaks CLIENT TYPE argument and TS.INFO parsing under Turkish/Azeri locales #4595

Closed

Description

@ggivo

Expected behavior

Redis wire tokens and reply parsing must not depend on the JVM default locale.

  • ClientType.REPLICA should always produce the wire token replica.
  • TS.INFO replies with duplicatePolicy: first / min should always parse to the corresponding DuplicatePolicy enum value.

Actual behavior

On a JVM whose default locale is Turkish or Azeri (tr_TR, az_AZ), ASCII case conversion of the letter i/I produces the dotted/dotless variants (İ/ı):

  • ClientType uses name().toLowerCase(), so REPLICA → replıca — CLIENT KILL/LIST TYPE replica sends an invalid token and fails with a server error.
  • TSInfo uses toUpperCase() before DuplicatePolicy.valueOf(...), so first → FİRST — valueOf throws, the exception is swallowed, and duplicatePolicy silently stays a String.

Steps to reproduce

Locale.setDefault(new Locale("tr", "TR"));
// 1) wire token (must run before ClientType is class-loaded)
System.out.println("REPLICA".toLowerCase());          // replıca
// 2) reply parsing
DuplicatePolicy.valueOf("first".toUpperCase());       // IllegalArgumentException: FİRST

Or run any client on a Turkish-locale JVM (-Duser.language=tr -Duser.country=TR) and call jedis.clientList(ClientType.REPLICA).

Context

Related issues #1319 fixed the same Turkish-locale problem for WITHSCORES, and GeoUnit, Protocol.ResponseKeyword, and

Environment

  • Jedis: master (affects all released versions)
  • Affects only JVMs with tr/az default locales; no impact on other locales

Activity

  1. added theissue type on Jul 7, 2026
  2. ggivo commented on Jul 7, 2026

    @ggivo
    CollaboratorAuthor
    Reproducible example
    class LocaleServerRepro {
    
        public static void main(String[] args) {
            Locale.setDefault(new Locale("tr", "TR"));
    
            String host = args.length > 0 ? args[0] : "localhost";
            int port = args.length > 1 ? Integer.parseInt(args[1]) : 6379;
    
            // 1) CLIENT LIST TYPE — wire token built from ClientType enum
            try (Jedis jedis = new Jedis(host, port)) {
                String result = jedis.clientList(ClientType.REPLICA);
                System.out.println("CLIENT LIST TYPE replica: OK"
                        + (result.isEmpty() ? " (no replica clients — expected on a plain server)" : "\n" + result));
            } catch (Exception e) {
                System.out.println("CLIENT LIST TYPE replica: FAILED — " + e.getClass().getSimpleName()
                        + ": " + e.getMessage());
            }
    
            // 2) TS.INFO duplicatePolicy — reply parsing
            try (RedisClient uj = RedisClient.create(host,port)) {
                String key = "locale:repro:ts";
                uj.del(key);
                uj.tsCreate(key, TSCreateParams.createParams().duplicatePolicy(DuplicatePolicy.FIRST));
    
                TSInfo info = uj.tsInfo(key);
                Object policy = info.getProperty("duplicatePolicy");
                System.out.println("TS.INFO duplicatePolicy: " + policy + " of type "
                        + policy.getClass().getSimpleName() + "   (expected: FIRST of type DuplicatePolicy)");
    
                uj.del(key);
            } catch (Exception e) {
                System.out.println("TS.INFO check skipped — " + e.getClass().getSimpleName()
                        + ": " + e.getMessage());
            }
        }
    
    }
    
    
  3. added 2 commits that reference this issue on Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions