apache/cassandra · error · ProtocolException
Invalid query kind in BATCH messages. Must be 0 or 1 but…
Error message
Invalid query kind in BATCH messages. Must be 0 or 1 but got
What it means
ProtocolException from BatchMessage.codec.decode when the per-query kind byte in a CQL BATCH frame is neither 0 (query string) nor 1 (prepared id). The client sent a malformed batch frame, so decoding fails and the connection is typically closed.
Solutions
- Fix the producer to write kind=0 for query strings and kind=1 for prepared IDs.
- Verify compression and framing configuration match between client and server (corruption shifts byte boundaries).
- Capture and hexdump the offending BATCH frame to confirm byte layout against the protocol spec.
- Use the official java driver's batch API instead of hand-encoding frames.
Example fix
// before body.writeByte(2); // invalid kind // after body.writeByte(0); // 0 = query string, 1 = prepared statement id
Defensive patterns
Strategy: validation
Validate before calling
// when hand-encoding a batch entry
int kind = stmt instanceof String ? 0 : 1;
if (kind != 0 && kind != 1) throw new IllegalArgumentException("kind must be 0 or 1"); Try / catch
try { batch(); } catch (ProtocolException e) { if (e.getMessage().contains("Invalid query kind")) dumpAndInspectFrame(); } Prevention
- Encode kind=0 for query strings, kind=1 for prepared IDs
- Prefer the java driver over hand-rolled frames
- Match compression settings to avoid byte-shift corruption
When it happens
Trigger: A malformed or corrupted BATCH frame whose per-statement kind byte is not 0 or 1 — typically a hand-rolled client, buggy proxy rewriting frames, or desynchronization from a bad length prefix earlier in the body.
Common situations: Custom native-protocol client implementations encoding the kind byte incorrectly; frame corruption from mismatched compression settings; fuzzing or version-mismatched middleware.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Invalid BATCH message type
- Invalid IP address ( . . . ) while deserializing inet…
- Invalid IP address while deserializing inet address
- Invalid value for the paging state
- Out of bound timestamp, must be in
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/d4335d9637787b17.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/transport/messages/BatchMessage.java:70
public class BatchMessage extends Message.Request
{
public static final Message.Codec<BatchMessage> codec = new Message.Codec<BatchMessage>()
{
public BatchMessage decode(ByteBuf body, ProtocolVersion version)
{
byte type = body.readByte();
int n = body.readUnsignedShort();
List<Object> queryOrIds = new ArrayList<>(n);
List<byte[][]> variables = new ArrayList<>(n);
for (int i = 0; i < n; i++)
{
byte kind = body.readByte();
if (kind == 0)
queryOrIds.add(CBUtil.readLongString(body));
else if (kind == 1)
queryOrIds.add(MD5Digest.wrap(CBUtil.readBytes(body)));
else
throw new ProtocolException("Invalid query kind in BATCH messages. Must be 0 or 1 but got " + kind);
variables.add(CBUtil.readValueListAsByteArrays(body, version));
}
QueryOptions options = QueryOptions.codec.decode(body, version);
return new BatchMessage(toType(type), queryOrIds, variables, options);
}
public void encode(BatchMessage msg, ByteBuf dest, ProtocolVersion version)
{
int queries = msg.queryOrIdList.size();
dest.writeByte(fromType(msg.batchType));
dest.writeShort(queries);
for (int i = 0; i < queries; i++)
{
Object q = msg.queryOrIdList.get(i);
dest.writeByte((byte)(q instanceof String ? 0 : 1));View on GitHub (pinned to 88fd0f6a0e)