apache/cassandra · error · MarshalException
The duration months, days and nanoseconds must be all of the
Error message
The duration months, days and nanoseconds must be all of the same sign (%d, %d, %d)
What it means
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.
Source
Thrown at src/java/org/apache/cassandra/serializers/DurationSerializer.java:101
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);
}
}
/**
* Checks that the specified {@code long} can be cast to an {@code int} without information lost.
*
* @param l the {@code long} to check
* @return {@code true} if the specified {@code long} can be cast to an {@code int} without information lost,
* {@code false} otherwise.
*/
private boolean canBeCastToInt(long l)
{View on GitHub (pinned to 88fd0f6a0e)
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
Example fix
// before Duration bad = Duration.newInstance(1, -2, 0); // mixed signs // after Duration ok = Duration.newInstance(-1, -2, 0); // all negative, allowed
Defensive patterns
Strategy: validation
Validate before calling
public static boolean sameSign(long a, long b, long c) {
boolean nonNeg = a >= 0 && b >= 0 && c >= 0;
boolean nonPos = a <= 0 && b <= 0 && c <= 0;
return nonNeg || nonPos;
}
// guard: if (!sameSign(months, days, nanos)) reject before serializing Type guard
public static boolean isValidDuration(Duration d) {
return sameSign(d.getMonths(), d.getDays(), d.getNanoseconds());
} Try / catch
try {
Duration d = DurationSerializer.instance.deserialize(buffer);
} catch (MarshalException e) {
if (e.getMessage().contains("same sign")) logger.warn("mixed-sign duration rejected");
} Prevention
- Normalize sign across components before Duration.newInstance
- Reject mixed-sign user input at the UI/parser layer
- Add unit tests covering negative durations
When it happens
Trigger: 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.
Common situations: User input like 'P1MT-2D' style strings parsed to mixed-sign values; application computing negative day offsets; driver/UDF passing arbitrary ints.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid value of auto_snapshot_ttl: ${conf.auto_snapshot_ttl
- Expected at least 3 bytes for a duration (%d)
- The duration months must be a 32 bits integer but was: %d
- The duration days must be a 32 bits integer but was: %d
- Not enough bytes to read a set
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/74f45119d250893d.
Report an issue: GitHub.