apache/kafka · error · IllegalArgumentException
Cannot serialize UNKNOWN control record type
Error message
Cannot serialize UNKNOWN control record type
What it means
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.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/ControlRecordType.java:77
private static final Logger log = LoggerFactory.getLogger(ControlRecordType.class);
private static final int CONTROL_RECORD_KEY_SIZE = 4;
private final short type;
private final ByteBuffer buffer;
ControlRecordType(short type) {
this.type = type;
ControlRecordTypeSchema schema = new ControlRecordTypeSchema().setType(type);
buffer = MessageUtil.toVersionPrefixedByteBuffer(ControlRecordTypeSchema.HIGHEST_SUPPORTED_VERSION, schema);
}
public short type() {
return type;
}
public ByteBuffer recordKey() {
if (this == UNKNOWN)
throw new IllegalArgumentException("Cannot serialize UNKNOWN control record type");
return buffer.duplicate();
}
public int controlRecordKeySize() {
return buffer.remaining();
}
public static short parseTypeId(ByteBuffer key) {
// We should duplicate the original buffer since it will be read again in some cases, for example,
// read by KafkaRaftClient and RaftClient.Listener
ByteBuffer buffer = key.duplicate();
if (buffer.remaining() < CONTROL_RECORD_KEY_SIZE)
throw new InvalidRecordException("Invalid value size found for control record key. " +
"Must have at least " + CONTROL_RECORD_KEY_SIZE + " bytes, but found only " + buffer.remaining());
short version = buffer.getShort();
if (version < ControlRecordTypeSchema.LOWEST_SUPPORTED_VERSION)
throw new InvalidRecordException("Invalid version found for control record: " + version +View on GitHub (pinned to c31c9215e1)
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.
Example fix
// before
ByteBuffer key = controlRecordType.recordKey();
// after
if (controlRecordType == ControlRecordType.UNKNOWN) {
throw new IllegalStateException("Refusing to re-serialise UNKNOWN control record");
}
ByteBuffer key = controlRecordType.recordKey(); Defensive patterns
Strategy: type-guard
Validate before calling
// Never serialize UNKNOWN; guard before calling recordKey()
import org.apache.kafka.common.record.ControlRecordType;
if (controlRecordType != ControlRecordType.UNKNOWN) {
ByteBuffer key = controlRecordType.recordKey();
} else {
// UNKNOWN is a sentinel for unrecognized types; it cannot be serialized
} Type guard
import org.apache.kafka.common.record.ControlRecordType;
static boolean isSerializable(ControlRecordType t) {
return t != ControlRecordType.UNKNOWN;
}
// usage: if (isSerializable(type)) { buf = type.recordKey(); } Try / catch
try {
ByteBuffer key = controlRecordType.recordKey();
} catch (IllegalArgumentException e) {
// attempted to serialize UNKNOWN; skip or reject the record
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Expected %s control record type(%d), but found %s
- Buffer underflow while parsing consumer protocol's header
- Malformed consumer protocol subscription
- Malformed consumer protocol assignment
- Invalid serialized transaction state format: {serializedStat
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/b5e18b565123bfba.json.
Report an issue: GitHub.