{"id":"c12e77d178f21f5c","repo":"apache/kafka","slug":"cannot-specify-a-negative-version-level","errorCode":null,"errorMessage":"Cannot specify a negative version level.","messagePattern":"Cannot specify a negative version level\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/admin/FeatureUpdate.java","lineNumber":78,"sourceCode":"\n    /**\n     * @param maxVersionLevel   The new maximum version level for the finalized feature.\n     *                          a value of zero is special and indicates that the update is intended to\n     *                          delete the finalized feature, and should be accompanied by setting\n     *                          the upgradeType to safe or unsafe.\n     * @param upgradeType     Indicate what kind of upgrade should be performed in this operation.\n     *                          - UPGRADE: upgrading the feature level\n     *                          - SAFE_DOWNGRADE: only downgrades which do not result in metadata loss are permitted\n     *                          - UNSAFE_DOWNGRADE: any downgrade, including those which may result in metadata loss, are permitted\n     */\n    public FeatureUpdate(final short maxVersionLevel, final UpgradeType upgradeType) {\n        if (maxVersionLevel == 0 && upgradeType.equals(UpgradeType.UPGRADE)) {\n            throw new IllegalArgumentException(String.format(\n                    \"The upgradeType flag should be set to SAFE_DOWNGRADE or UNSAFE_DOWNGRADE when the provided maxVersionLevel:%d is < 1.\",\n                    maxVersionLevel));\n        }\n        if (maxVersionLevel < 0) {\n            throw new IllegalArgumentException(\"Cannot specify a negative version level.\");\n        }\n        this.maxVersionLevel = maxVersionLevel;\n        this.upgradeType = upgradeType;\n    }\n\n    public short maxVersionLevel() {\n        return maxVersionLevel;\n    }\n\n    public UpgradeType upgradeType() {\n        return upgradeType;\n    }\n\n    @Override\n    public boolean equals(Object other) {\n        if (this == other) {\n            return true;\n        }","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/admin/FeatureUpdate.java#L60-L96","documentation":"Thrown by the FeatureUpdate constructor when maxVersionLevel is negative. Version levels are non-negative shorts where 0 is the delete sentinel and positive values are real feature levels; a negative value is never valid and indicates a bug or arithmetic underflow in the caller. The check runs after the maxVersionLevel==0 UPGRADE guard so it catches any value < 0.","triggerScenarios":"Constructing new FeatureUpdate((short) -1, anyType), or passing a short computed from a subtraction, cast, or external input that went negative. Reached from admin clients, tooling, or deserialization paths that build a FeatureUpdate from untrusted numbers.","commonSituations":"Caller computes (currentLevel - 1) without clamping; short underflow when reading raw bytes; malformed external config feeding a negative value; test fixtures with wrong literals; client parsing a corrupted controller response.","solutions":["Clamp computed level values to >= 0 before constructing the FeatureUpdate.","Validate input from external sources (config, RPC, CLI args) is non-negative before passing it in.","Use 0 explicitly when you intend deletion (with the appropriate downgrade type).","Add a unit test asserting FeatureUpdate rejects negative levels to catch regressions."],"exampleFix":"// before\nshort target = (short) (currentLevel - offset); // can be negative\nnew FeatureUpdate(target, FeatureUpdate.UpgradeType.UPGRADE);\n\n// after\nshort target = (short) Math.max(0, currentLevel - offset);\nFeatureUpdate.UpgradeType type = target == 0\n    ? FeatureUpdate.UpgradeType.SAFE_DOWNGRADE\n    : FeatureUpdate.UpgradeType.UPGRADE;\nnew FeatureUpdate(target, type);","handlingStrategy":"validation","validationCode":"// maxVersionLevel must be >= 0. Reject negatives before constructing.\nshort maxVersionLevel = ...;\nif (maxVersionLevel < 0) {\n    throw new IllegalArgumentException(\n        \"maxVersionLevel must be >= 0, got \" + maxVersionLevel);\n}\nFeatureUpdate update = new FeatureUpdate(maxVersionLevel, upgradeType);","typeGuard":"// Guard: only non-negative shorts are valid FeatureUpdate levels.\nstatic boolean isNonNegativeLevel(short level) {\n    return level >= 0;\n}","tryCatchPattern":"try {\n    FeatureUpdate update = new FeatureUpdate(maxVersionLevel, upgradeType);\n} catch (IllegalArgumentException e) {\n    // Negative level reached the constructor. Clamp or reject upstream.\n    log.error(\"Negative FeatureUpdate level {}: {}\", maxVersionLevel, e.getMessage());\n}","preventionTips":["Validate numeric inputs at the API/config boundary before they reach FeatureUpdate.","Do not cast arbitrary int/long config values to short without a range check; negative ints truncate to negative shorts.","Centralize FeatureUpdate construction behind a validator so the rule lives in one place.","Add a precondition (e.g. Guava checkArgument) at the entry point that produces maxVersionLevel."],"tags":["admin-client","feature-versioning","api-misuse","illegal-argument","input-validation"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}