apache/kafka · error · IllegalArgumentException
Unexpected RecordLevel id `%d`, it should be between `%d` an
Error message
Unexpected RecordLevel id `%d`, it should be between `%d` and `%d` (inclusive)
What it means
Thrown by Sensor.RecordingLevel.forId(int) when deserializing a recording level from a numeric id that falls outside the valid range [0, MAX_RECORDING_LEVEL_KEY] (currently 0=INFO, 1=DEBUG, 2=TRACE). The library uses stable numeric ids to serialize RecordingLevel on the wire, so an out-of-range value means the sender and receiver disagree on the protocol enum. It is an IllegalArgumentException to surface the corrupt/unrecognized value as early as possible rather than indexing into ID_TO_TYPE with a bad subscript.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/metrics/Sensor.java:114
}
ID_TO_TYPE = idToName;
MAX_RECORDING_LEVEL_KEY = maxRL;
}
/** an english description of the api--this is for debugging and can change */
public final String name;
/** the permanent and immutable id of an API--this can't change ever */
public final short id;
RecordingLevel(int id, String name) {
this.id = (short) id;
this.name = name;
}
public static RecordingLevel forId(int id) {
if (id < MIN_RECORDING_LEVEL_KEY || id > MAX_RECORDING_LEVEL_KEY)
throw new IllegalArgumentException(String.format("Unexpected RecordLevel id `%d`, it should be between `%d` " +
"and `%d` (inclusive)", id, MIN_RECORDING_LEVEL_KEY, MAX_RECORDING_LEVEL_KEY));
return ID_TO_TYPE[id];
}
/** Case insensitive lookup by protocol name */
public static RecordingLevel forName(String name) {
return RecordingLevel.valueOf(name.toUpperCase(Locale.ROOT));
}
public boolean shouldRecord(final int configId) {
if (configId == INFO.id) {
return this.id == INFO.id;
} else if (configId == DEBUG.id) {
return this.id == INFO.id || this.id == DEBUG.id;
} else if (configId == TRACE.id) {
return true;
} else {
throw new IllegalStateException("Did not recognize recording level " + configId);View on GitHub (pinned to c31c9215e1)
Solutions
- Validate or clamp the id against RecordingLevel.MAX_RECORDING_LEVEL_KEY before calling forId, and reject/log unknown ids at the deserialization boundary.
- Align client and broker to the same Kafka version so the RecordingLevel enum (and its id mapping) matches on both sides.
- If the id originates from user config, accept the string name (INFO/DEBUG/TRACE) and use RecordingLevel.forName(name) instead of a raw numeric id.
- When forking/customizing, add the missing enum constant in RecordingLevel so the id maps to a real level.
Example fix
// before
RecordingLevel level = RecordingLevel.forId(rawIdFromWire);
// after
if (rawIdFromWire < 0 || rawIdFromWire > RecordingLevel.MAX_RECORDING_LEVEL_KEY) {
throw new IllegalStateException("Unknown recording level id from peer: " + rawIdFromWire);
}
RecordingLevel level = RecordingLevel.forId(rawIdFromWire); Defensive patterns
Strategy: validation
Validate before calling
int id = /* from protocol/config */;
if (id < 0 || id > Sensor.RecordingLevel.MAX_RECORDING_LEVEL_KEY) {
// reject or default instead of calling forId
throw new IllegalArgumentException("Invalid recording level id: " + id);
}
Sensor.RecordingLevel level = Sensor.RecordingLevel.forId(id); Type guard
static Sensor.RecordingLevel safeRecordingLevelForId(int id) {
for (Sensor.RecordingLevel rl : Sensor.RecordingLevel.values()) {
if (rl.id == id) return rl;
}
return null;
} Prevention
- Prefer Sensor.RecordingLevel.forName(name) over forId(int) when the source is configuration text.
- Treat raw protocol ints as untrusted; validate against Sensor.RecordingLevel.values() before use.
- Whitelist the id against the enum constants rather than assuming a fixed numeric range.
When it happens
Trigger: Calling RecordingLevel.forId(id) with id < 0 or id > 2. Typically reached when a RecordingLevel is deserialized from a byte buffer/protocol struct (e.g. metrics-related request/response fields, or a config parsed from a numeric string) and the remote side sent a value not matching any INFO/DEBUG/TRACE id.
Common situations: Cross-version communication where a newer client/broker introduces a new RecordingLevel id that the older peer does not know; corrupted or manually-crafted protocol payloads; tests that construct a RecordingLevel from a hard-coded magic number that drifted from the enum. Also seen when a configuration property accepting a recording-level id is fed an arbitrary integer.
Related errors
- Buffer underflow while parsing response for request with hea
- Buffer underflow while parsing consumer protocol's header
- Malformed consumer protocol subscription
- Malformed consumer protocol assignment
- Unexpected error code {} while fetching at offset {} from to
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/9139b238e1c30af7.json.
Report an issue: GitHub.