apache/cassandra · error · ProtocolException
Invalid message version. Got %s but previous messages on thi
Error message
Invalid message version. Got %s but previous messages on this connection had version %s
What it means
ProtocolException thrown by the pre-v5 Envelope.Decoder when a frame's protocol version differs from the version already established on that connection. Native protocol versions are negotiated once (via STARTUP) and must remain constant for the connection's lifetime; a mid-connection version change means a desynchronized or malicious stream, so the frame is rejected.
Source
Thrown at src/java/org/apache/cassandra/transport/PreV5Handlers.java:295
/**
* Simple adaptor to allow {@link org.apache.cassandra.transport.Message.Decoder#decodeMessage(Channel, Envelope)}
* to be used as a handler in pre-V5 pipelines
*/
@ChannelHandler.Sharable
public static class ProtocolDecoder extends MessageToMessageDecoder<Envelope>
{
public static final ProtocolDecoder instance = new ProtocolDecoder();
private ProtocolDecoder(){}
public void decode(ChannelHandlerContext ctx, Envelope source, List<Object> results)
{
try
{
ProtocolVersion version = getConnectionVersion(ctx);
if (source.header.version != version)
{
throw new ProtocolException(
String.format("Invalid message version. Got %s but previous " +
"messages on this connection had version %s",
source.header.version, version));
}
results.add(Message.Decoder.decodeMessage(ctx.channel(), source));
}
catch (Throwable ex)
{
source.release();
// Remember the streamId
throw ErrorMessage.wrap(ex, source.header.streamId);
}
}
}
/**
* Simple adaptor to plug CQL message encoding into pre-V5 pipelines
*/View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Reconnect so the version is renegotiated via STARTUP and kept consistent.
- Fix the client to use the negotiated ProtocolVersion object for every frame instead of a hardcoded version byte.
- Check intermediaries (proxies, TLS offloaders) that may rewrite or corrupt frame headers.
- Update the driver to a version that pins frames to the negotiated version.
Example fix
// before: hardcoded version byte in client frame writer out.writeByte(3); // v3 regardless of negotiation // after: write the negotiated version out.writeByte(connection.getProtocolVersion().asInt());
Defensive patterns
Strategy: validation
Validate before calling
// client: assert every frame uses the negotiated version checkState(negotiatedVersion == frameVersion, "frame version %s != negotiated %s", frameVersion, negotiatedVersion);
Try / catch
try { decode(buf); } catch (ProtocolException e) { if (e.getMessage().contains("Invalid message version")) { reconnectWithRenegotiation(); } } Prevention
- Always write frames with the negotiated ProtocolVersion, never a hardcoded byte
- Investigate proxies if version bytes change mid-stream
- Reconnect immediately — version changes cannot be recovered mid-connection
When it happens
Trigger: On a pre-v5 connection, decode obtains the connection's negotiated version (getConnectionVersion) and finds source.header.version != that version — e.g. after byte-stream desync, or a client suddenly sending frames with a different version byte than STARTUP declared.
Common situations: Stream corruption from proxies/NAT devices, clients that hardcode a version byte differing from their negotiated version, fuzzers, or mixing driver instances over a shared connection.
Related errors
- Received frame with CUSTOM_PAYLOAD flag for native protocol
- Invalid or unsupported protocol version (%d); supported vers
- native_transport_max_frame_size must be positive value < %dB
- Unknown code %d for a consistency level
- Wrong protocol direction (expected %s, got %s) for opcode %d
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/bb41b457588ac42d.
Report an issue: GitHub.