apache/kafka · error · IllegalArgumentException

Expected %s control record type(%d), but found %s

Error message

Expected %s control record type(%d), but found %s

What it means

ControlRecordUtils.validateControlRecordType (line 95) throws IllegalArgumentException when a control record's parsed type does not match the type the caller expected. Each typed deserialise helper (deserializeLeaderChangeMessage, deserializeSnapshotHeaderRecord, deserializeSnapshotFooterRecord, deserializeKRaftVersionRecord, deserializeVotersRecord) calls it as a guard so it only decodes the schema it understands.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/ControlRecordUtils.java:95

    public static KRaftVersionRecord deserializeKRaftVersionRecord(ByteBuffer data) {
        return new KRaftVersionRecord(new ByteBufferAccessor(data.slice()), KRAFT_VERSION_CURRENT_VERSION);
    }

    public static VotersRecord deserializeVotersRecord(Record record) {
        ControlRecordType recordType = ControlRecordType.parse(record.key());
        validateControlRecordType(ControlRecordType.KRAFT_VOTERS, recordType);

        return deserializeVotersRecord(record.value());
    }

    public static VotersRecord deserializeVotersRecord(ByteBuffer data) {
        return new VotersRecord(new ByteBufferAccessor(data.slice()), KRAFT_VOTERS_CURRENT_VERSION);
    }

    private static void validateControlRecordType(ControlRecordType expected, ControlRecordType actual) {
        if (actual != expected) {
            throw new IllegalArgumentException(
                String.format(
                    "Expected %s control record type(%d), but found %s",
                    expected,
                    expected.type(),
                    actual
                )
            );
        }
    }
}

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Parse record.key() first and dispatch by the actual ControlRecordType before calling the typed deserialiser.
  2. Confirm the controller/broker feature/version aligns between writer and reader.
  3. Re-fetch the snapshot or metadata log from a healthy source if records are misordered.

Example fix

// before
LeaderChangeMessage msg = ControlRecordUtils.deserializeLeaderChangeMessage(record);
// after
ControlRecordType type = ControlRecordType.parse(record.key());
if (type != ControlRecordType.LEADER_CHANGE) {
    throw new IllegalStateException("Unexpected control type " + type);
}
LeaderChangeMessage msg = ControlRecordUtils.deserializeLeaderChangeMessage(record);
Defensive patterns

Strategy: validation

Validate before calling

// Parse the control record type yourself and compare to the expected type before deserializing
import org.apache.kafka.common.record.ControlRecordType;
import org.apache.kafka.common.record.ControlRecordUtils;
import org.apache.kafka.common.record.Record;

ControlRecordType actual = ControlRecordType.parse(record.key());
if (actual == ControlRecordType.LEADER_CHANGE) {
    // safe: type matches the specific deserializer
    var msg = ControlRecordUtils.deserializeLeaderChangeMessage(record);
} else {
    // wrong control record type for this deserializer; route to the correct handler
}

Type guard

import org.apache.kafka.common.record.ControlRecordType;
import org.apache.kafka.common.record.Record;

static boolean isControlRecordType(Record r, ControlRecordType expected) {
    return ControlRecordType.parse(r.key()) == expected;
}

// usage: if (isControlRecordType(record, ControlRecordType.SNAPSHOT_HEADER)) {
//            ControlRecordUtils.deserializeSnapshotHeaderRecord(record);
//        }

Try / catch

try {
    var msg = ControlRecordUtils.deserializeSnapshotHeaderRecord(record);
} catch (IllegalArgumentException e) {
    // record was not the expected control record type; dispatch to the matching deserializer
}

Prevention

When it happens

Trigger: Calling one of ControlRecordUtils.deserializeXxx(record) on a record whose key parses to a different ControlRecordType; e.g. feeding a COMMIT/ABORT record into deserializeLeaderChangeMessage; processing a control batch whose record ordering differs from the reader's assumption.

Common situations: KRaft snapshot or leader-change handling reading the wrong record slot; controller/follower version or feature mismatch; misrouted control records; tests using the wrong control record fixture.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/eae8b04d8202e335.json. Report an issue: GitHub.