apache/cassandra · error · ProtocolException
Unexpected request expecting READY, AUTHENTICATE, ERROR or…
Error message
Unexpected %s request expecting READY, AUTHENTICATE, ERROR or SUPPORTED
What it means
During protocol negotiation the client's InitialHandler decoder receives an envelope whose message type is not one of READY, AUTHENTICATE, ERROR or SUPPORTED, and throws a ProtocolException. Only those four response types are legal while the connection is still in the STARTUP/negotiating state; anything else indicates a protocol framing or state bug.
Solutions
- Log the received message type to identify what the server actually sent.
- Remove any intermediary proxies or test a direct connection to the node.
- Pin an explicit protocol version in STARTUP options instead of auto-negotiation.
- Ensure the pipeline matches the negotiated version (legacy vs modern) and compression settings match the server.
Defensive patterns
Strategy: validation
Validate before calling
// after each response during handshake
Set<Message.Type> allowed = EnumSet.of(Message.Type.READY, Message.Type.AUTHENTICATE, Message.Type.ERROR, Message.Type.SUPPORTED);
if (!allowed.contains(resp.type)) throw new IllegalStateException("Unexpected handshake response " + resp.type); Try / catch
try { handshake(); } catch (ProtocolException e) { log.warn("Negotiation failure, retrying direct connection", e); connectDirect(); } Prevention
- Avoid intermediary proxies during protocol negotiation
- Pin an explicit protocol version
- Match compression/framing to the negotiated version
When it happens
Trigger: Server replies to OPTIONS/STARTUP with an unexpected message type — e.g. a request frame arrives on the legacy pipeline during handshake, or a proxy/mixed-version node answers out of spec.
Common situations: Connecting through a broken or misbehaving proxy; version negotiation glitches between very old/new protocol versions; corruption from a wrong compression/frame-decoder configuration in the pipeline.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Connection Error
- Invalid BATCH message type
- Invalid query kind in BATCH messages. Must be 0 or 1 but…
- Out of bound timestamp, must be in
- Unexpected error
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/6af3191f8d34f4a4.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/transport/SimpleClient.java:478
if (request.header.version.isGreaterOrEqualTo(ProtocolVersion.V5))
{
configureModernPipeline(ctx, request, largeMessageThreshold);
// consuming the message is done when setting up the pipeline
}
else
{
configureLegacyPipeline(ctx);
// really just removes self from the pipeline, so pass this message on
ctx.pipeline().context(Envelope.Decoder.class).fireChannelRead(request);
}
break;
case SUPPORTED:
case ERROR:
// just pass through
results.add(request);
break;
default:
throw new ProtocolException(String.format("Unexpected %s request expecting " +
"READY, AUTHENTICATE, ERROR or SUPPORTED",
request.header.type));
}
}
private void configureModernPipeline(ChannelHandlerContext ctx, Envelope request, int largeMessageThreshold)
{
logger.info("Configuring modern pipeline");
ChannelPipeline pipeline = ctx.pipeline();
pipeline.remove(HandlerNames.ENVELOPE_DECODER);
pipeline.remove(HandlerNames.MESSAGE_DECODER);
pipeline.remove(HandlerNames.MESSAGE_ENCODER);
pipeline.remove(HandlerNames.RESPONSE_HANDLER);
BufferPoolAllocator allocator = GlobalBufferPoolAllocator.instance;
Channel channel = ctx.channel();
channel.config().setOption(ChannelOption.ALLOCATOR, allocator);
int queueCapacity = 1 << 20; // 1MiBView on GitHub (pinned to 88fd0f6a0e)