apache/kafka · error · IllegalStateException
Did not recognize recording level {configId}
Error message
Did not recognize recording level {configId} What it means
Thrown by RecordingLevel.shouldRecord(int configId) when configId does not equal INFO.id, DEBUG.id, or TRACE.id. Unlike forId, this is an IllegalStateException because shouldRecord is given a configId that the enum switch cannot match, which the code treats as an impossible/internal-consistency condition rather than user input. It guards the metric-recording gating logic that decides whether a sensor emits at the configured verbosity.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/metrics/Sensor.java:132
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);
}
}
}
private final RecordingLevel recordingLevel;
Sensor(Metrics registry, String name, Sensor[] parents, MetricConfig config, Time time,
long inactiveSensorExpirationTimeSeconds, RecordingLevel recordingLevel) {
super();
this.registry = registry;
this.name = Objects.requireNonNull(name);
this.parents = parents == null ? new Sensor[0] : parents;
this.metrics = new LinkedHashMap<>();
this.stats = new ArrayList<>();
this.config = config;
this.time = time;
this.inactiveSensorExpirationTimeMs = TimeUnit.MILLISECONDS.convert(inactiveSensorExpirationTimeSeconds, TimeUnit.SECONDS);
this.lastRecordTime = time.milliseconds();View on GitHub (pinned to c31c9215e1)
Solutions
- Ensure MetricConfig.recordLevel() always returns a RecordingLevel obtained from RecordingLevel.forName / RecordingLevel.forId, never a hand-built id.
- If you build MetricConfig in tests, set the recordLevel from the enum constant (e.g. RecordingLevel.INFO) rather than poking an id field.
- When extending RecordingLevel with a new level, add the matching branch to shouldRecord so the new id is recognized.
- Add a guard before calling shouldRecord to validate the configured id is within [0, MAX_RECORDING_LEVEL_KEY].
Example fix
// before MetricConfig cfg = new MetricConfig().recordLevel(/* raw int */ 5); // after MetricConfig cfg = new MetricConfig().recordLevel(RecordingLevel.DEBUG);
Defensive patterns
Strategy: validation
Validate before calling
int configId = /* from wire/config */;
if (configId != Sensor.RecordingLevel.INFO.id
&& configId != Sensor.RecordingLevel.DEBUG.id
&& configId != Sensor.RecordingLevel.TRACE.id) {
// unknown recording level; do not call shouldRecord(configId)
throw new IllegalArgumentException("Unknown recording level configId: " + configId);
} Type guard
static boolean isKnownRecordingLevelConfigId(int configId) {
return configId == Sensor.RecordingLevel.INFO.id
|| configId == Sensor.RecordingLevel.DEBUG.id
|| configId == Sensor.RecordingLevel.TRACE.id;
} Prevention
- Only ever pass ids taken from the Sensor.RecordingLevel enum constants.
- Never invent configId values; they must match an existing RecordingLevel.id exactly.
- If you hit this in production, suspect version skew between client and broker recording levels.
When it happens
Trigger: A Sensor's shouldRecord() is invoked when the configured MetricConfig.recordLevel().id is not 0, 1, or 2. Because shouldRecord(configId) only has explicit branches for INFO/DEBUG/TRACE ids, any other numeric configId (negative, or > 2) lands in the else branch.
Common situations: A custom or mock MetricConfig returning a fabricated recordLevel id; memory corruption or uninitialized config in a test; a future RecordingLevel id added to the enum but the switch in shouldRecord not updated; deserialized config whose recordLevel was read with the wrong type width.
Related errors
- Telemetry is not enabled. Set config `enable.metrics.push` t
- Circular dependency in sensors: {name} is its own parent.
- The maximum value {max} must be greater than the minimum val
- Must be at least 1 bucket
- The frequency centered at '{centerValue}' is not within the
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/d9c91a62c758630a.json.
Report an issue: GitHub.