apache/cassandra · error · org.apache.cassandra.transport.ProtocolException
Unknown opcode
Error message
Unknown opcode %d
What it means
Message.Type.fromOpcode maps the frame header's 1-byte opcode to a message type. It throws this ProtocolException when the opcode exceeds the opcode table size or has no registered type, meaning the frame is not a valid native-protocol message. A related error is thrown when the opcode's direction doesn't match (e.g. a REQUEST opcode arriving on a response path).
Solutions
- Fix client framing so each frame's header (version, flags, opcode, length) matches the actual body; resync the stream if bytes were lost.
- Verify client and server support the same protocol version (header version byte must match).
- Check for proxies/load balancers corrupting or re-segmenting the CQL stream.
- Capture the raw bytes of the offending frame to identify the bad opcode and source.
Example fix
// before: writing opcode as raw arbitrary value header[4] = (byte) 0x7F; // bogus opcode // after header[4] = Opcode.QUERY; // use a defined opcode constant (0x07)
Defensive patterns
Strategy: try-catch
Validate before calling
// validate opcode before writing a frame header
if (opcode < 0 || opcode >= 0x0E || Opcode.fromOpcode(opcode) == null)
throw new IllegalArgumentException("Invalid opcode: " + opcode); Type guard
boolean isKnownOpcode(int op) { return op >= 0 && op < 0x0E; } // valid native protocol opcodes are 0x00-0x0D Try / catch
try { connect(); } catch (ProtocolException e) {
if (e.getMessage().startsWith("Unknown opcode")) {
log.error("Frame desync or bogus opcode; reconnect and reset stream", e);
}
} Prevention
- Only use opcode constants from the driver, never raw numbers.
- Ensure frame lengths in headers exactly match written body sizes to avoid stream desync.
- Keep client and server protocol versions compatible.
- Watch for intermediaries (proxies, LBs) corrupting or re-segmenting the TCP stream.
When it happens
Trigger: A frame header contains an opcode not in the protocol's opcode table (e.g. 0x30+), or a null entry in opcodeIdx — from corrupt streams, wrong protocol framing, or garbage bytes after a desync.
Common situations: TCP stream desync (a proxy or half-written client misframes messages); fuzzing; a client speaking an incompatible/imagined protocol version; bytes injected mid-stream.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- e.getMessage()
- Event " + eventType.name() + " not valid for protocol…
- Invalid IP address ( . . . ) while deserializing inet…
- Invalid IP address while deserializing inet address
- Provided frame does not appear to be LZ4 compressed
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/e3f33a2a23c58af9.
Report an issue: GitHub.
Appendix: source
Thrown at src/java/org/apache/cassandra/transport/Message.java:140
for (Type type : Type.values())
{
if (opcodeIdx[type.opcode] != null)
throw new IllegalStateException("Duplicate opcode");
opcodeIdx[type.opcode] = type;
}
}
Type(int opcode, Direction direction, Codec<?> codec)
{
this.opcode = opcode;
this.direction = direction;
this.codec = codec;
}
public static Type fromOpcode(int opcode, Direction direction)
{
if (opcode >= opcodeIdx.length)
throw new ProtocolException(String.format("Unknown opcode %d", opcode));
Type t = opcodeIdx[opcode];
if (t == null)
throw new ProtocolException(String.format("Unknown opcode %d", opcode));
if (t.direction != direction)
throw new ProtocolException(String.format("Wrong protocol direction (expected %s, got %s) for opcode %d (%s)",
t.direction,
direction,
opcode,
t));
return t;
}
@VisibleForTesting
public Codec<?> unsafeSetCodec(Codec<?> codec) throws NoSuchFieldException, IllegalAccessException
{
Codec<?> original = this.codec;
Field field = Type.class.getDeclaredField("codec");
field.setAccessible(true);View on GitHub (pinned to 88fd0f6a0e)