redis/jedis · error · JedisException
Unknown message type
Error message
Unknown message type: <reply>
What it means
JedisPubSubBase.process() reads replies from the subscription loop and dispatches them by message type (subscribe ack, message, pong, etc.). When a reply arrives that does not match any known pub/sub reply shape, it throws JedisException("Unknown message type: " + reply). It means the connection delivered an object the pub/sub state machine cannot interpret, usually a stray non-pub/sub reply on the subscription connection.
Solutions
- Use a dedicated, freshly-obtained connection for pub/sub — never one with outstanding commands or unread replies.
- Ensure subscribe/psubscribe is the first command issued on the pub/sub connection.
- Check protocol version (RESP2 vs RESP3) matches what the pub/sub implementation expects.
- Catch JedisException, close and re-create the subscription connection.
Example fix
// before
Jedis j = pool.getResource();
j.set("k","v"); // reply still pending
new JedisPubSubBase(j).proceed(channel); // desync -> Unknown message type
// after
try (Jedis j = pool.getResource()) {
j.set("k","v"); // complete within try so reply consumed
}
try (Jedis j = pool.getResource()) {
new JedisPubSubBase(j).proceed(channel); // clean connection
} Defensive patterns
Strategy: try-catch
Validate before calling
if (jedis.isSubscribed() || jedis.isBroken()) { jedis.close(); jedis = pool.getResource(); } Type guard
boolean isPubSubReply(Object reply) {
return reply instanceof List && !((List<?>) reply).isEmpty()
&& ((List<?>) reply).get(0) instanceof byte[];
} Try / catch
try {
pubSub.proceed(jedis, channel);
} catch (JedisException e) {
if (e.getMessage().startsWith("Unknown message type")) {
jedis.close();
// re-subscribe on a fresh connection
} else throw e;
} Prevention
- Always dedicate a fresh connection to pub/sub; subscribe must be the first command.
- Consume/complete all prior command replies before entering the subscribe loop.
- Never share the pub/sub connection across threads.
- Keep client and server protocol (RESP2/RESP3) consistent.
When it happens
Trigger: Calling proceed()/proceedWithPatterns() on a connection whose reply stream contains a plain (non-array) object that is not a recognized pub/sub reply type, e.g. a plain byte[] status reply when no resultHandler is registered (that path is checked elsewhere) or a List whose first element is not a known command name byte[].
Common situations: Reusing a Jedis connection for subscribe while a pending command reply (e.g. AUTH or SELECT ack) is still unread; mixing a shared/pipelined connection into pub/sub; protocol desync after an error mid-handshake.
Related errors
- Unexpected message
- Unknown message type
- Blocking pub/sub operations are not supported on…
- Unknown message type
- Unexpected message
AI-assisted analysis of redis/jedis@6dac31d4c2 (2026-09-08).
Data as JSON: /api/errors/42591cf75aeb0abb.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/redis/clients/jedis/JedisPubSubBase.java:198
subscribedChannels = ((Long) listReply.get(2)).intValue();
final byte[] bpattern = (byte[]) listReply.get(1);
final T enpattern = (bpattern == null) ? null : encode(bpattern);
onPUnsubscribe(enpattern, subscribedChannels);
} else if (Arrays.equals(PONG.getRaw(), resp)) {
final byte[] bpattern = (byte[]) listReply.get(1);
final T enpattern = (bpattern == null) ? null : encode(bpattern);
onPong(enpattern);
} else {
throw new JedisException("Unknown message type: " + firstObj);
}
} else if (reply instanceof byte[]) {
Consumer<Object> resultHandler = authenticator.resultHandler.poll();
if (resultHandler == null) {
throw new JedisException("Unexpected message : " + SafeEncoder.encode((byte[]) reply));
}
resultHandler.accept(reply);
} else {
throw new JedisException("Unknown message type: " + reply);
}
} while (!Thread.currentThread().isInterrupted() && isSubscribed());
// /* Invalidate instance since this thread is no longer listening */
// this.client = null;
}
private void processPingReply(Object reply) {
byte[] resp = (byte[]) reply;
if ("PONG".equals(SafeEncoder.encode(resp))) {
onPong(null);
} else {
onPong(encode(resp));
}
}
}
View on GitHub (pinned to 6dac31d4c2)