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

  1. Fix the producer to write kind=0 for query strings and kind=1 for prepared IDs.
  2. Verify compression and framing configuration match between client and server (corruption shifts byte boundaries).
  3. Capture and hexdump the offending BATCH frame to confirm byte layout against the protocol spec.
  4. 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

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


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)