redis/jedis · critical · JedisConnectionException
Unknown reply:
Error message
Unknown reply:
What it means
Protocol.process() reads the first byte of a Redis reply and dispatches on the RESP type. If the byte is not one of the known reply markers (+, -, :, $, *, _, #, etc.), it throws JedisConnectionException "Unknown reply: X", meaning the bytes on the wire are not a valid RESP reply.
Solutions
- Verify the host/port points to a real Redis server (run redis-cli -h host -p port PING).
- Reset/recreate the connection if a desync occurred; never share a Connection across threads.
- Check for middleboxes/proxies or TLS misconfiguration returning HTML or plain text.
- Upgrade Jedis — new Redis reply types can be unknown to older client versions.
Example fix
// before
Jedis jedis = new Jedis("myapp.internal", 443); // plain-text proxy replies -> Unknown reply
// after
Jedis jedis = new Jedis("redis.internal", 6379); // correct Redis host/port, or use ssl=true for TLS endpoints Defensive patterns
Strategy: try-catch
Validate before calling
// before connecting, sanity-check the endpoint answers like Redis // e.g. run: redis-cli -h host -p port PING -> expect PONG
Try / catch
try {
return jedis.get(key);
} catch (JedisConnectionException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Unknown reply")) {
// stream desync or non-Redis endpoint: discard connection, verify endpoint, reconnect
jedis.close();
}
throw e;
} Prevention
- Never share a Jedis/Connection object across threads.
- Confirm endpoints with redis-cli PING before wiring them into apps.
- Check proxies/TLS termination aren't injecting plain-text responses.
- Keep Jedis updated for newly introduced reply types.
When it happens
Trigger: Deserializing a reply whose leading byte is unrecognized: responses to unknown protocol input, corrupted stream desynchronization (e.g. reading a reply mid-stream), or an unexpected char such as 'C'/'O' from a non-Redis or proxy endpoint.
Common situations: Pointing the client at something that is not a Redis server, a proxy (HAProxy/Caddy) emitting plain text, RESP3/push-message desync, or custom scripts intercepting traffic.
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
- Bulk reply length is less than expected
- Unknown protocol version:
- Unexpected character!
- Attempting to write to a broken connection.
- Attempting to read from a broken connection.
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/e9480bfb8bd3d5fb.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/Protocol.java:175
return processMultiBulkReply(is);
case GREATER_THAN_BYTE:
// Process push message through the consumer chain
PushMessage message = processPush(is, pushConsumer);
if (message != null) {
// Message not consumed by PushConsumers - propagate to application
// This preserves backward compatibility by allowing applications to handle
// push messages that aren't consumed by internal consumers
return message.getContent();
} else {
// Message was consumed by PushConsumers - continue reading next
break;
}
case MINUS_BYTE:
processError(is);
return null;
// TODO: Blob error '!'
default:
throw new JedisConnectionException("Unknown reply: " + (char) b);
}
} while (true);
}
private static byte[] processBulkReply(final RedisInputStream is) {
return processBulkReply(is, 0);
}
/**
* Process a bulk reply, optionally skipping a prefix.
* @param is the input stream
* @param skipBytes number of bytes to skip at the beginning (used for verbatim strings)
* @return the bulk reply data (excluding skipped bytes), or null if length is -1
*/
private static byte[] processBulkReply(final RedisInputStream is, final int skipBytes) {
final int len = is.readIntCrLf();
if (len == -1) {View on GitHub (pinned to 6dac31d4c2)