{"record":{"id":"bf1c961fe42b0114","repo":"redis/jedis","slug":"unexpected-character","errorCode":null,"errorMessage":"Unexpected character!","messagePattern":"Unexpected character!","errorType":"exception","errorClass":"JedisConnectionException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/util/RedisInputStream.java","lineNumber":71,"sourceCode":"\n  public byte readByte() throws JedisConnectionException {\n    ensureFill();\n    return buf[count++];\n  }\n\n  private void ensureCrLf() {\n    final byte[] buf = this.buf;\n\n    ensureFill();\n    if (buf[count++] == '\\r') {\n\n      ensureFill();\n      if (buf[count++] == '\\n') {\n        return;\n      }\n    }\n\n    throw new JedisConnectionException(\"Unexpected character!\");\n  }\n\n  public String readLine() {\n    final StringBuilder sb = new StringBuilder();\n    while (true) {\n      ensureFill();\n\n      byte b = buf[count++];\n      if (b == '\\r') {\n        ensureFill(); // Must be one more byte\n\n        byte c = buf[count++];\n        if (c == '\\n') {\n          break;\n        }\n        sb.append((char) b);\n        sb.append((char) c);\n      } else {","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/util/RedisInputStream.java#L53-L89","documentation":"RedisInputStream.ensureCrLf consumes bytes after a CR until it finds the LF that terminates a CRLF reply line. If the byte following \\r is not \\n, the reply stream is malformed, so the parser aborts with a JedisConnectionException. This indicates a corrupted or non-RESP stream between client and server.","triggerScenarios":"Thrown from ensureCrLf (called by readNullCrLf and readBooleanCrLf) when the byte after '\\r' is not '\\n' while parsing a bulk-string null reply or a boolean reply.","commonSituations":"A proxy, TLS terminator, or middlebox mangling the RESP stream; reading from a socket that is not speaking RESP; a desynchronized connection reused after a prior parse failure.","solutions":["Close and reopen the connection (return it to the pool as broken / destroy it) — the stream offset is unrecoverable.","Verify no intermediate proxy or firewall is altering the Redis protocol stream.","Confirm the endpoint is actually a Redis server speaking RESP2/RESP3.","Upgrade Jedis to ensure you are on a version compatible with your server's RESP protocol version."],"exampleFix":"// before\ntry {\n  String val = jedis.get(key);\n} catch (JedisConnectionException e) {\n  // connection kept in pool with corrupt stream\n}\n// after\ntry {\n  String val = jedis.get(key);\n} catch (JedisConnectionException e) {\n  jedis.close(); // or pool.returnBrokenResource(jedis)\n  val = jedisRetry.get(key);\n}","handlingStrategy":"try-catch","validationCode":"if (connectionDesyncSuspected) { throw new IllegalStateException(\"discard connection before reuse\"); }","typeGuard":null,"tryCatchPattern":"try {\n  return jedis.get(key);\n} catch (JedisConnectionException e) {\n  jedis.close(); // return broken resource\n  return retryOnFreshConnection(() -> jedisPool.getResource().get(key));\n}","preventionTips":["Always discard connections after any protocol/parse exception — never reuse them.","Avoid middleboxes that rewrite TCP payloads on the Redis path.","Keep Jedis and Redis server protocol versions (RESP2/RESP3) compatible."],"tags":["protocol","connection","redis"],"backgroundTag":"invalid-json-response","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"}