{"id":"b5e18b565123bfba","repo":"apache/kafka","slug":"cannot-serialize-unknown-control-record-type","errorCode":null,"errorMessage":"Cannot serialize UNKNOWN control record type","messagePattern":"Cannot serialize UNKNOWN control record type","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/record/internal/ControlRecordType.java","lineNumber":77,"sourceCode":"    private static final Logger log = LoggerFactory.getLogger(ControlRecordType.class);\n    private static final int CONTROL_RECORD_KEY_SIZE = 4;\n\n    private final short type;\n    private final ByteBuffer buffer;\n\n    ControlRecordType(short type) {\n        this.type = type;\n        ControlRecordTypeSchema schema = new ControlRecordTypeSchema().setType(type);\n        buffer = MessageUtil.toVersionPrefixedByteBuffer(ControlRecordTypeSchema.HIGHEST_SUPPORTED_VERSION, schema);\n    }\n\n    public short type() {\n        return type;\n    }\n\n    public ByteBuffer recordKey() {\n        if (this == UNKNOWN)\n            throw new IllegalArgumentException(\"Cannot serialize UNKNOWN control record type\");\n        return buffer.duplicate();\n    }\n\n    public int controlRecordKeySize() {\n        return buffer.remaining();\n    }\n\n    public static short parseTypeId(ByteBuffer key) {\n        // We should duplicate the original buffer since it will be read again in some cases, for example,\n        // read by KafkaRaftClient and RaftClient.Listener\n        ByteBuffer buffer = key.duplicate();\n        if (buffer.remaining() < CONTROL_RECORD_KEY_SIZE)\n            throw new InvalidRecordException(\"Invalid value size found for control record key. \" +\n                    \"Must have at least \" + CONTROL_RECORD_KEY_SIZE + \" bytes, but found only \" + buffer.remaining());\n\n        short version = buffer.getShort();\n        if (version < ControlRecordTypeSchema.LOWEST_SUPPORTED_VERSION)\n            throw new InvalidRecordException(\"Invalid version found for control record: \" + version +","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/record/internal/ControlRecordType.java#L59-L95","documentation":"ControlRecordType.recordKey() (line 77) refuses to serialise the UNKNOWN sentinel. UNKNOWN (typeId -1) is a catch-all for control record typeIds the client does not recognise and is intended only for inbound parsing; it has no on-wire serialised form. Re-emitting it would produce a meaningless key, so the library blocks it with IllegalArgumentException.","triggerScenarios":"Calling ControlRecordType.UNKNOWN.recordKey(); building a control record batch whose type came from fromTypeId() of an unrecognised id and then writing it back out; generic code that round-trips any parsed ControlRecordType through a producer/mirror.","commonSituations":"Mirror/replication tooling that re-emits control records of newer types an older client doesn't know; code that treats UNKNOWN as a real type.","solutions":["Filter out or skip UNKNOWN before serialising (treat it as 'ignore').","Map the unknown typeId to a concrete known type, or drop the record instead of re-keying it.","Upgrade the producing client to a version that recognises the typeId so it parses to a concrete type."],"exampleFix":"// before\nByteBuffer key = controlRecordType.recordKey();\n// after\nif (controlRecordType == ControlRecordType.UNKNOWN) {\n    throw new IllegalStateException(\"Refusing to re-serialise UNKNOWN control record\");\n}\nByteBuffer key = controlRecordType.recordKey();","handlingStrategy":"type-guard","validationCode":"// Never serialize UNKNOWN; guard before calling recordKey()\nimport org.apache.kafka.common.record.ControlRecordType;\nif (controlRecordType != ControlRecordType.UNKNOWN) {\n    ByteBuffer key = controlRecordType.recordKey();\n} else {\n    // UNKNOWN is a sentinel for unrecognized types; it cannot be serialized\n}","typeGuard":"import org.apache.kafka.common.record.ControlRecordType;\n\nstatic boolean isSerializable(ControlRecordType t) {\n    return t != ControlRecordType.UNKNOWN;\n}\n\n// usage: if (isSerializable(type)) { buf = type.recordKey(); }","tryCatchPattern":"try {\n    ByteBuffer key = controlRecordType.recordKey();\n} catch (IllegalArgumentException e) {\n    // attempted to serialize UNKNOWN; skip or reject the record\n}","preventionTips":["UNKNOWN is a read-only sentinel returned by fromTypeId() for unrecognized type ids; it is never serializable.","If you build control records from external input, reject UNKNOWN before attempting recordKey().","Avoid using ControlRecordType.UNKNOWN as a placeholder/default; pick the concrete type you intend to write."],"tags":["control-record","serialization","kraft"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}