{"record":{"id":"0093566c58fda1a0","repo":"apache/cassandra","slug":"the-duration-months-must-be-a-32-bits-integer-but","errorCode":null,"errorMessage":"The duration months must be a 32 bits integer but was: %d","messagePattern":"The duration months must be a 32 bits integer but was: (.+?)","errorType":"validation","errorClass":"MarshalException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/serializers/DurationSerializer.java","lineNumber":92,"sourceCode":"        {\n            // this should never happen with a DataInputBuffer\n            throw new AssertionError(\"Unexpected error\", e);\n        }\n    }\n\n    public <V> void validate(V value, ValueAccessor<V> accessor) throws MarshalException\n    {\n        if (accessor.size(value) < 3)\n            throw new MarshalException(String.format(\"Expected at least 3 bytes for a duration (%d)\", accessor.size(value)));\n\n        try (DataInputBuffer in = new DataInputBuffer(accessor.toBuffer(value), true))  // FIXME: value input buffer\n        {\n            long monthsAsLong = in.readVInt();\n            long daysAsLong = in.readVInt();\n            long nanoseconds = in.readVInt();\n\n            if (!canBeCastToInt(monthsAsLong))\n                throw new MarshalException(String.format(\"The duration months must be a 32 bits integer but was: %d\",\n                                                         monthsAsLong));\n            if (!canBeCastToInt(daysAsLong))\n                throw new MarshalException(String.format(\"The duration days must be a 32 bits integer but was: %d\",\n                                                         daysAsLong));\n            int months = (int) monthsAsLong;\n            int days = (int) daysAsLong;\n\n            if (!((months >= 0 && days >= 0 && nanoseconds >= 0) || (months <= 0 && days <=0 && nanoseconds <=0)))\n                throw new MarshalException(String.format(\"The duration months, days and nanoseconds must be all of the same sign (%d, %d, %d)\",\n                                                         months, days, nanoseconds));\n        }\n        catch (IOException e)\n        {\n            // this should never happen with a DataInputBuffer\n            throw new AssertionError(\"Unexpected error\", e);\n        }\n    }\n","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/serializers/DurationSerializer.java#L74-L110","documentation":"After decoding the vints, validate() checks the months field fits in a signed 32-bit int. Vints can carry larger values, so an out-of-int-range months component cannot be represented as Duration's int fields and is rejected with this MarshalException.","triggerScenarios":"validate/deserialize of a duration whose encoded months vint exceeds Integer.MAX_VALUE or is below Integer.MIN_VALUE — typically hand-crafted or corrupted bytes, since normal Duration construction uses ints.","commonSituations":"Corrupted SSTable bytes decoded as huge vints; manually packing vints with values beyond int range; fuzz/malformed input on user-supplied duration strings via native protocol.","solutions":["Ensure durations are constructed via Duration.newInstance(int months, int days, long nanos) which enforces int months","Re-serialize with the official serializer instead of hand-packed vints","Check data integrity if value came from storage","If input comes from user strings, validate ranges before conversion"],"exampleFix":"// before\nbyte[] packed = packVInt(Long.valueOf(Integer.MAX_VALUE) + 1); // months out of int range\n// after\nDuration d = Duration.newInstance(12, 0, 0); // months fits int\nByteBuffer ok = DurationSerializer.instance.serialize(d);","handlingStrategy":"validation","validationCode":"public static boolean monthsFitsInt(long months) {\n    return months >= Integer.MIN_VALUE && months <= Integer.MAX_VALUE;\n}","typeGuard":"public static boolean isEncodableDuration(long months, long days, long nanos) {\n    return monthsFitsInt(months) && days >= Integer.MIN_VALUE && days <= Integer.MAX_VALUE;\n}","tryCatchPattern":"try {\n    serializer.validate(buffer, accessor);\n} catch (MarshalException e) {\n    if (e.getMessage().contains(\"months must be a 32 bits\")) logger.error(\"months overflow\");\n}","preventionTips":["Keep month components small (Cassandra limits to signed int anyway)","Parse duration strings via Duration.from which enforces bounds","Fuzz-test any custom duration parsing"],"tags":["serialization","cassandra","duration","value-out-of-range"],"backgroundTag":"value-out-of-range","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}