{"record":{"id":"190c236adec9cc02","repo":"apache/cassandra","slug":"unhandled-checkstatusreply-kind","errorCode":null,"errorMessage":"Unhandled CheckStatusReply kind: ","messagePattern":"Unhandled CheckStatusReply kind: ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java","lineNumber":206,"sourceCode":"            KeySerializers.nullableRoutingKey.serialize(ok.homeKey, out);\n            CommandSerializers.invalidIf.serialize(ok.invalidIf, out);\n\n            if (!(reply instanceof CheckStatusOkFull))\n                return;\n\n            CheckStatusOkFull okFull = (CheckStatusOkFull) ok;\n            CommandSerializers.nullablePartialTxn.serialize(okFull.partialTxn, out, version);\n            DepsSerializers.nullablePartialDeps.serialize(okFull.stableDeps, out);\n            CommandSerializers.nullableWrites.serialize(okFull.writes, out, version);\n        }\n\n        @Override\n        public CheckStatusReply deserialize(DataInputPlus in, Version version) throws IOException\n        {\n            byte kind = in.readByte();\n            switch (kind)\n            {\n                default: throw new IOException(\"Unhandled CheckStatusReply kind: \" + Integer.toHexString(Byte.toUnsignedInt(kind)));\n                case NACK:\n                    return CheckStatusNack.NotOwned;\n                case OK:\n                case FULL:\n                    KnownMap map = knownMap.deserialize(in);\n                    SaveStatus maxKnowledgeStatus = CommandSerializers.saveStatus.deserialize(in);\n                    SaveStatus maxStatus = CommandSerializers.saveStatus.deserialize(in);\n                    Ballot maxPromised = CommandSerializers.ballot.deserialize(in);\n                    Ballot maxAcceptedOrCommitted = CommandSerializers.ballot.deserialize(in);\n                    Ballot acceptedOrCommitted = CommandSerializers.ballot.deserialize(in);\n                    Timestamp executeAt = ExecuteAtSerializer.deserializeNullable(in);\n                    boolean isCoordinating = in.readBoolean();\n                    Durability durability = CommandSerializers.durability.deserialize(in);\n                    Route<?> route = KeySerializers.nullableRoute.deserialize(in);\n                    RoutingKey homeKey = KeySerializers.nullableRoutingKey.deserialize(in);\n                    Infer.InvalidIf invalidIf = CommandSerializers.invalidIf.deserialize(in);\n\n                    if (kind == OK)","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/service/accord/serializers/CheckStatusSerializers.java#L188-L224","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncase NACK: return CheckStatusNack.NotOwned;\ncase OK:\ncase FULL: ...\n// after\ndefault: throw new IOException(\"Unhandled CheckStatusReply kind: \" + Integer.toHexString(Byte.toUnsignedInt(kind)));\n// add a case for any newly introduced kind, e.g.\ncase NEW_KIND: return CheckStatusNewReply.deserialize(in);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    CheckStatusReply reply = CheckStatusReply.Serializer.deserialize(in, version);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Unhandled CheckStatusReply kind\")) {\n        logger.warn(\"Incompatible/unknown CheckStatusReply from peer; dropping message\", e);\n        // drop or request re-send; do not retry on same misaligned stream\n    } else throw e;\n}","preventionTips":["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."],"tags":["serialization","deserialization","accord","wire-format"],"backgroundTag":"unsupported-enum-value","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}