{"record":{"id":"af880f2028224f5b","repo":"redis/jedis","slug":"unexpected-message-reply","errorCode":null,"errorMessage":"Unexpected message : <reply>","messagePattern":"Unexpected message : <reply>","errorType":"exception","errorClass":"JedisException","httpStatus":null,"severity":"error","filePath":"src/main/java/redis/clients/jedis/JedisPubSubBase.java","lineNumber":194,"sourceCode":"          final byte[] bpattern = (byte[]) listReply.get(1);\n          final T enpattern = (bpattern == null) ? null : encode(bpattern);\n          onPSubscribe(enpattern, subscribedChannels);\n        } else if (Arrays.equals(PUNSUBSCRIBE.getRaw(), resp)) {\n          subscribedChannels = ((Long) listReply.get(2)).intValue();\n          final byte[] bpattern = (byte[]) listReply.get(1);\n          final T enpattern = (bpattern == null) ? null : encode(bpattern);\n          onPUnsubscribe(enpattern, subscribedChannels);\n        } else if (Arrays.equals(PONG.getRaw(), resp)) {\n          final byte[] bpattern = (byte[]) listReply.get(1);\n          final T enpattern = (bpattern == null) ? null : encode(bpattern);\n          onPong(enpattern);\n        } else {\n          throw new JedisException(\"Unknown message type: \" + firstObj);\n        }\n      } else if (reply instanceof byte[]) {\n        Consumer<Object> resultHandler = authenticator.resultHandler.poll();\n        if (resultHandler == null) {\n          throw new JedisException(\"Unexpected message : \" + SafeEncoder.encode((byte[]) reply));\n        }\n        resultHandler.accept(reply);\n      } else {\n        throw new JedisException(\"Unknown message type: \" + reply);\n      }\n    } while (!Thread.currentThread().isInterrupted() && isSubscribed());\n\n    //    /* Invalidate instance since this thread is no longer listening */\n    //    this.client = null;\n  }\n\n  private void processPingReply(Object reply) {\n    byte[] resp = (byte[]) reply;\n    if (\"PONG\".equals(SafeEncoder.encode(resp))) {\n      onPong(null);\n    } else {\n      onPong(encode(resp));\n    }","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/redis/jedis/blob/6dac31d4c224fb3257c216f3985340c6f500cdcb/src/main/java/redis/clients/jedis/JedisPubSubBase.java#L176-L212","documentation":"When a reply arrives as a raw byte[] (rather than a list), process() assumes it is the response to an earlier one-shot command queued via a resultHandler. If the handler queue is empty, there is no one expecting this reply, so Jedis throws this JedisException ('Unexpected message : ...') (JedisPubSubBase.java:194).","triggerScenarios":"A bare byte[] reply is read in the pub/sub loop with no pending resultHandler — e.g. a late/stray command reply, a ping response after its consumer was already consumed, or protocol desync between issued commands and read replies.","commonSituations":"Calling commands on the subscribed connection asynchronously while the loop runs; prior exceptions skipped replies leaving stale bytes in the buffer; proxies injecting extra responses.","solutions":["Do not run regular commands on the connection while the pub/sub loop is active; use a separate connection from the pool.","On desync, close and reopen the connection (the stream state is unrecoverable).","Upgrade Jedis if you mix RESP3 push messages, which changed reply dispatch handling."],"exampleFix":"// before\njedis.set(\"k\", \"v\");        // on subscribed connection\npubSub.proceed(jedis, \"ch\"); // stray SET reply -> Unexpected message\n\n// after\ntry (Jedis other = pool.getResource()) { other.set(\"k\", \"v\"); }\npubSub.proceed(jedis, \"ch\");","handlingStrategy":"try-catch","validationCode":"// do not run regular commands on a connection inside a subscribe loop;\n// acquire a separate connection from the pool for one-shot commands","typeGuard":null,"tryCatchPattern":"try {\n  pubSub.proceed(jedis, channel);\n} catch (JedisException e) {\n  if (e.getMessage().startsWith(\"Unexpected message :\")) {\n    // protocol desync: close and reopen connection\n    jedis.close();\n    jedis = pool.getResource();\n    pubSub.proceed(jedis, channel);\n  }\n}","preventionTips":["Keep one-shot commands off the pub/sub connection; use pool.getResource() for them.","After any exception in the loop, discard the connection — buffer state may be desynced.","Avoid custom/proxy layers that inject extra replies into RESP streams."],"tags":["java","redis","pubsub","connection","protocol-desync"],"backgroundTag":"unexpected-response-shape","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"}