redis/jedis · error · JedisConnectionException
Unexpected character!
Error message
Unexpected character!
What it means
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.
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.
Example fix
// before
try {
String val = jedis.get(key);
} catch (JedisConnectionException e) {
// connection kept in pool with corrupt stream
}
// after
try {
String val = jedis.get(key);
} catch (JedisConnectionException e) {
jedis.close(); // or pool.returnBrokenResource(jedis)
val = jedisRetry.get(key);
} Defensive patterns
Strategy: try-catch
Validate before calling
if (connectionDesyncSuspected) { throw new IllegalStateException("discard connection before reuse"); } Try / catch
try {
return jedis.get(key);
} catch (JedisConnectionException e) {
jedis.close(); // return broken resource
return retryOnFreshConnection(() -> jedisPool.getResource().get(key));
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/bf1c961fe42b0114.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/util/RedisInputStream.java:71
public byte readByte() throws JedisConnectionException {
ensureFill();
return buf[count++];
}
private void ensureCrLf() {
final byte[] buf = this.buf;
ensureFill();
if (buf[count++] == '\r') {
ensureFill();
if (buf[count++] == '\n') {
return;
}
}
throw new JedisConnectionException("Unexpected character!");
}
public String readLine() {
final StringBuilder sb = new StringBuilder();
while (true) {
ensureFill();
byte b = buf[count++];
if (b == '\r') {
ensureFill(); // Must be one more byte
byte c = buf[count++];
if (c == '\n') {
break;
}
sb.append((char) b);
sb.append((char) c);
} else {View on GitHub (pinned to 6dac31d4c2)