{"record":{"id":"74f45119d250893d","repo":"apache/cassandra","slug":"the-duration-months-days-and-nanoseconds-must-be","errorCode":null,"errorMessage":"The duration months, days and nanoseconds must be all of the same sign (%d, %d, %d)","messagePattern":"The duration months, days and nanoseconds must be all of the same sign \\((.+?), (.+?), (.+?)\\)","errorType":"validation","errorClass":"MarshalException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/serializers/DurationSerializer.java","lineNumber":101,"sourceCode":"            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\n    /**\n     * Checks that the specified {@code long} can be cast to an {@code int} without information lost.\n     *\n     * @param l the {@code long} to check\n     * @return {@code true} if the specified {@code long} can be cast to an {@code int} without information lost,\n     * {@code false} otherwise.\n     */\n    private boolean canBeCastToInt(long l)\n    {","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/serializers/DurationSerializer.java#L83-L119","documentation":"Cassandra durations must have months, days and nanoseconds all non-negative or all non-positive; mixed-sign components are rejected. validate() enforces this after decoding and reports the three parsed values in the message.","triggerScenarios":"validate/deserialize of a duration whose decoded components have differing signs — e.g. Duration.newInstance(1, -2, 0), negative nanoseconds with positive months, or corrupted bytes decoding to mixed signs.","commonSituations":"User input like 'P1MT-2D' style strings parsed to mixed-sign values; application computing negative day offsets; driver/UDF passing arbitrary ints.","solutions":["Normalize the duration so all components share the same sign before serializing","Clamp or reject negative components at the application boundary","If data came from storage, fix offending rows via scrub/migration","Use Duration.from(String) which enforces sign constraints from text input"],"exampleFix":"// before\nDuration bad = Duration.newInstance(1, -2, 0); // mixed signs\n// after\nDuration ok = Duration.newInstance(-1, -2, 0); // all negative, allowed","handlingStrategy":"validation","validationCode":"public static boolean sameSign(long a, long b, long c) {\n    boolean nonNeg = a >= 0 && b >= 0 && c >= 0;\n    boolean nonPos = a <= 0 && b <= 0 && c <= 0;\n    return nonNeg || nonPos;\n}\n// guard: if (!sameSign(months, days, nanos)) reject before serializing","typeGuard":"public static boolean isValidDuration(Duration d) {\n    return sameSign(d.getMonths(), d.getDays(), d.getNanoseconds());\n}","tryCatchPattern":"try {\n    Duration d = DurationSerializer.instance.deserialize(buffer);\n} catch (MarshalException e) {\n    if (e.getMessage().contains(\"same sign\")) logger.warn(\"mixed-sign duration rejected\");\n}","preventionTips":["Normalize sign across components before Duration.newInstance","Reject mixed-sign user input at the UI/parser layer","Add unit tests covering negative durations"],"tags":["serialization","cassandra","duration","validation"],"backgroundTag":"invalid-argument-value","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"}