apache/cassandra · error · MarshalException

The duration months must be a 32 bits integer but was

Error message

The duration months must be a 32 bits integer but was: %d

What it means

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.

Solutions

  1. Ensure durations are constructed via Duration.newInstance(int months, int days, long nanos) which enforces int months
  2. Re-serialize with the official serializer instead of hand-packed vints
  3. Check data integrity if value came from storage
  4. If input comes from user strings, validate ranges before conversion

Example fix

// before
byte[] packed = packVInt(Long.valueOf(Integer.MAX_VALUE) + 1); // months out of int range
// after
Duration d = Duration.newInstance(12, 0, 0); // months fits int
ByteBuffer ok = DurationSerializer.instance.serialize(d);
Defensive patterns

Strategy: validation

Validate before calling

public static boolean monthsFitsInt(long months) {
    return months >= Integer.MIN_VALUE && months <= Integer.MAX_VALUE;
}

Type guard

public static boolean isEncodableDuration(long months, long days, long nanos) {
    return monthsFitsInt(months) && days >= Integer.MIN_VALUE && days <= Integer.MAX_VALUE;
}

Try / catch

try {
    serializer.validate(buffer, accessor);
} catch (MarshalException e) {
    if (e.getMessage().contains("months must be a 32 bits")) logger.error("months overflow");
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/0093566c58fda1a0. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/serializers/DurationSerializer.java:92

        {
            // this should never happen with a DataInputBuffer
            throw new AssertionError("Unexpected error", e);
        }
    }

    public <V> void validate(V value, ValueAccessor<V> accessor) throws MarshalException
    {
        if (accessor.size(value) < 3)
            throw new MarshalException(String.format("Expected at least 3 bytes for a duration (%d)", accessor.size(value)));

        try (DataInputBuffer in = new DataInputBuffer(accessor.toBuffer(value), true))  // FIXME: value input buffer
        {
            long monthsAsLong = in.readVInt();
            long daysAsLong = in.readVInt();
            long nanoseconds = in.readVInt();

            if (!canBeCastToInt(monthsAsLong))
                throw new MarshalException(String.format("The duration months must be a 32 bits integer but was: %d",
                                                         monthsAsLong));
            if (!canBeCastToInt(daysAsLong))
                throw new MarshalException(String.format("The duration days must be a 32 bits integer but was: %d",
                                                         daysAsLong));
            int months = (int) monthsAsLong;
            int days = (int) daysAsLong;

            if (!((months >= 0 && days >= 0 && nanoseconds >= 0) || (months <= 0 && days <=0 && nanoseconds <=0)))
                throw new MarshalException(String.format("The duration months, days and nanoseconds must be all of the same sign (%d, %d, %d)",
                                                         months, days, nanoseconds));
        }
        catch (IOException e)
        {
            // this should never happen with a DataInputBuffer
            throw new AssertionError("Unexpected error", e);
        }
    }

View on GitHub (pinned to 88fd0f6a0e)