{"id":"9139b238e1c30af7","repo":"apache/kafka","slug":"unexpected-recordlevel-id-d-it-should-be-betwe","errorCode":null,"errorMessage":"Unexpected RecordLevel id `%d`, it should be between `%d` and `%d` (inclusive)","messagePattern":"Unexpected RecordLevel id `(.+?)`, it should be between `(.+?)` and `(.+?)` \\(inclusive\\)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/metrics/Sensor.java","lineNumber":114,"sourceCode":"            }\n            ID_TO_TYPE = idToName;\n            MAX_RECORDING_LEVEL_KEY = maxRL;\n        }\n\n        /** an english description of the api--this is for debugging and can change */\n        public final String name;\n\n        /** the permanent and immutable id of an API--this can't change ever */\n        public final short id;\n\n        RecordingLevel(int id, String name) {\n            this.id = (short) id;\n            this.name = name;\n        }\n\n        public static RecordingLevel forId(int id) {\n            if (id < MIN_RECORDING_LEVEL_KEY || id > MAX_RECORDING_LEVEL_KEY)\n                throw new IllegalArgumentException(String.format(\"Unexpected RecordLevel id `%d`, it should be between `%d` \" +\n                    \"and `%d` (inclusive)\", id, MIN_RECORDING_LEVEL_KEY, MAX_RECORDING_LEVEL_KEY));\n            return ID_TO_TYPE[id];\n        }\n\n        /** Case insensitive lookup by protocol name */\n        public static RecordingLevel forName(String name) {\n            return RecordingLevel.valueOf(name.toUpperCase(Locale.ROOT));\n        }\n\n        public boolean shouldRecord(final int configId) {\n            if (configId == INFO.id) {\n                return this.id == INFO.id;\n            } else if (configId == DEBUG.id) {\n                return this.id == INFO.id || this.id == DEBUG.id;\n            } else if (configId == TRACE.id) {\n                return true;\n            } else {\n                throw new IllegalStateException(\"Did not recognize recording level \" + configId);","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/metrics/Sensor.java#L96-L132","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nRecordingLevel level = RecordingLevel.forId(rawIdFromWire);\n\n// after\nif (rawIdFromWire < 0 || rawIdFromWire > RecordingLevel.MAX_RECORDING_LEVEL_KEY) {\n    throw new IllegalStateException(\"Unknown recording level id from peer: \" + rawIdFromWire);\n}\nRecordingLevel level = RecordingLevel.forId(rawIdFromWire);","handlingStrategy":"validation","validationCode":"int id = /* from protocol/config */;\nif (id < 0 || id > Sensor.RecordingLevel.MAX_RECORDING_LEVEL_KEY) {\n    // reject or default instead of calling forId\n    throw new IllegalArgumentException(\"Invalid recording level id: \" + id);\n}\nSensor.RecordingLevel level = Sensor.RecordingLevel.forId(id);","typeGuard":"static Sensor.RecordingLevel safeRecordingLevelForId(int id) {\n    for (Sensor.RecordingLevel rl : Sensor.RecordingLevel.values()) {\n        if (rl.id == id) return rl;\n    }\n    return null;\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["metrics","protocol","serialization","version-mismatch"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}