{"record":{"id":"09805e0b5e12e245","repo":"redis/jedis","slug":"null-value-cannot-be-sent-to-redis","errorCode":null,"errorMessage":"null value cannot be sent to redis","messagePattern":"null value cannot be sent to redis","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/util/SafeEncoder.java","lineNumber":29,"sourceCode":"public final class SafeEncoder {\n\n  public static volatile Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;\n\n  private SafeEncoder() {\n    throw new InstantiationError(\"Must not instantiate this class\");\n  }\n\n  public static byte[][] encodeMany(final String... strs) {\n    byte[][] many = new byte[strs.length][];\n    for (int i = 0; i < strs.length; i++) {\n      many[i] = encode(strs[i]);\n    }\n    return many;\n  }\n\n  public static byte[] encode(final String str) {\n    if (str == null) {\n      throw new IllegalArgumentException(\"null value cannot be sent to redis\");\n    }\n    return str.getBytes(DEFAULT_CHARSET);\n  }\n\n  public static String encode(final byte[] data) {\n    return new String(data, DEFAULT_CHARSET);\n  }\n\n  /**\n   * This method takes an object and will convert all bytes[] and list of byte[] and will encode the\n   * object in a recursive way.\n   * @param dataToEncode\n   * @return the object fully encoded\n   */\n  public static Object encodeObject(Object dataToEncode) {\n    if (dataToEncode instanceof byte[]) {\n      return SafeEncoder.encode((byte[]) dataToEncode);\n    }","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/util/SafeEncoder.java#L11-L47","documentation":"SafeEncoder.encode(String) rejects null input with IllegalArgumentException because a null cannot be encoded into bytes for the Redis protocol. Jedis treats sending null values as an API misuse rather than silently serializing them as empty or \"null\" strings.","triggerScenarios":"Passing a null String to any Jedis command parameter that is encoded via SafeEncoder.encode — e.g., jedis.set(key, null), encodeMany with a null element, or building command arguments from nullable variables.","commonSituations":"Values read from configuration/HTTP/DB that were never null-checked before being sent to Redis; map lookups (map.get) returning null; optional fields absent in payloads.","solutions":["Null-check the value before the command and decide explicitly: skip, use a default, or delete the key.","For 'absent means delete', use DEL instead of SET with null.","Sanitize inputs at the boundary (validate map/config values) before reaching Redis calls.","Use Optional or a helper that converts null to a sentinel only if your data model intends it."],"exampleFix":"// before\nString value = props.get(key);\njedis.set(key, value); // NPE risk -> IllegalArgumentException from SafeEncoder\n// after\nString value = props.get(key);\nif (value != null) {\n  jedis.set(key, value);\n} else {\n  jedis.del(key);\n}","handlingStrategy":"type-guard","validationCode":"if (str == null) {\n  throw new IllegalArgumentException(\"Refusing to send null to redis; key=\" + key);\n}","typeGuard":"static boolean isSendable(String s) {\n  return s != null;\n}\n// usage\nif (isSendable(value)) { jedis.set(key, value); } else { jedis.del(key); }","tryCatchPattern":null,"preventionTips":["Null-check values sourced from maps, configs, or external payloads before Redis calls.","Decide explicit semantics for absent values (skip vs. DEL vs. default).","Prefer Optional.withFallback at input boundaries."],"tags":["null-check","argument-validation","api-misuse"],"backgroundTag":"null-argument","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"}