apache/cassandra · error · IOException
Unhandled CheckStatusReply kind:
Error message
Unhandled CheckStatusReply kind:
What it means
CheckStatusReply.Serializer.deserialize reads a one-byte kind tag and switches on it; any byte that is not a known CheckStatusReply kind (NACK, OK, FULL, etc.) hits the default branch and throws an IOException. This indicates the incoming byte stream does not match the wire format this version of the code understands — usually corruption, a truncated/misaligned stream, or a peer running an incompatible version that serializes a kind this node does not know.
Source
Thrown at src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java:206
KeySerializers.nullableRoutingKey.serialize(ok.homeKey, out);
CommandSerializers.invalidIf.serialize(ok.invalidIf, out);
if (!(reply instanceof CheckStatusOkFull))
return;
CheckStatusOkFull okFull = (CheckStatusOkFull) ok;
CommandSerializers.nullablePartialTxn.serialize(okFull.partialTxn, out, version);
DepsSerializers.nullablePartialDeps.serialize(okFull.stableDeps, out);
CommandSerializers.nullableWrites.serialize(okFull.writes, out, version);
}
@Override
public CheckStatusReply deserialize(DataInputPlus in, Version version) throws IOException
{
byte kind = in.readByte();
switch (kind)
{
default: throw new IOException("Unhandled CheckStatusReply kind: " + Integer.toHexString(Byte.toUnsignedInt(kind)));
case NACK:
return CheckStatusNack.NotOwned;
case OK:
case FULL:
KnownMap map = knownMap.deserialize(in);
SaveStatus maxKnowledgeStatus = CommandSerializers.saveStatus.deserialize(in);
SaveStatus maxStatus = CommandSerializers.saveStatus.deserialize(in);
Ballot maxPromised = CommandSerializers.ballot.deserialize(in);
Ballot maxAcceptedOrCommitted = CommandSerializers.ballot.deserialize(in);
Ballot acceptedOrCommitted = CommandSerializers.ballot.deserialize(in);
Timestamp executeAt = ExecuteAtSerializer.deserializeNullable(in);
boolean isCoordinating = in.readBoolean();
Durability durability = CommandSerializers.durability.deserialize(in);
Route<?> route = KeySerializers.nullableRoute.deserialize(in);
RoutingKey homeKey = KeySerializers.nullableRoutingKey.deserialize(in);
Infer.InvalidIf invalidIf = CommandSerializers.invalidIf.deserialize(in);
if (kind == OK)View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Verify all cluster nodes run a Cassandra version whose CheckStatusReply kind enum matches (complete rolling upgrade).
- Log the hex kind byte and compare against the current CheckStatusReply kinds to identify which value the peer sent.
- If a new kind was added to CheckStatusReply, add a matching case to CheckStatusSerializers so older/newer nodes can decode it.
- Check for earlier deserialization errors in the same connection that could have misaligned the stream; restart the failed message/session.
Example fix
// before
case NACK: return CheckStatusNack.NotOwned;
case OK:
case FULL: ...
// after
default: throw new IOException("Unhandled CheckStatusReply kind: " + Integer.toHexString(Byte.toUnsignedInt(kind)));
// add a case for any newly introduced kind, e.g.
case NEW_KIND: return CheckStatusNewReply.deserialize(in); Defensive patterns
Strategy: try-catch
Try / catch
try {
CheckStatusReply reply = CheckStatusReply.Serializer.deserialize(in, version);
} catch (IOException e) {
if (e.getMessage().startsWith("Unhandled CheckStatusReply kind")) {
logger.warn("Incompatible/unknown CheckStatusReply from peer; dropping message", e);
// drop or request re-send; do not retry on same misaligned stream
} else throw e;
} Prevention
- Keep all cluster nodes on serializer-compatible versions before enabling Accord traffic.
- Watch upgrade-safety of CheckStatusReply when adding new kinds.
- Log unexpected kind bytes with peer address and version for fast triage.
When it happens
Trigger: DataInputPlus stream containing a kind byte outside the enumerated CheckStatusReply kind values when deserializing a CheckStatusReply, e.g. during Accord inter-node message deserialization after a version mismatch or stream corruption.
Common situations: Rolling upgrades where a newer node sends a CheckStatusReply kind an older node cannot decode; network corruption or deserialization misalignment from a previously failed read; debugging/tests feeding synthetic bytes.
Related errors
- Cannot serialize RedundantStatus larger than 0xFFFF. Require
- Corrupted input: expected byte 1, 2, 3, 4, 5 or 6; received
- Corrupted input: expected byte 1, 2, 3, 4 or 5; received
- Invalid decimal value, expecting at least 4 bytes but got
- Invalid 64-bits double value, expecting 8 bytes but got
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/190c236adec9cc02.
Report an issue: GitHub.