apache/cassandra · error · IllegalArgumentException
Unable to convert '%s' to a duration
Error message
Unable to convert '%s' to a duration
What it means
Duration.from(String) in Cassandra's CQL types layer routes any string starting with 'P' (after an optional leading '-') to parseIso8601Format, which requires the whole string to match the ISO 8601 designator pattern P[n]Y[n]M[n]DT[n]H[n]M[n]S. If the string does not fully match, an IllegalArgumentException with this message is thrown. It means the value looked like it intended to be ISO 8601 but is malformed for that grammar.
Source
Thrown at src/java/org/apache/cassandra/cql3/functions/types/Duration.java:160
boolean isNegative = input.startsWith("-");
String source = isNegative ? input.substring(1) : input;
if (source.startsWith("P"))
{
if (source.endsWith("W")) return parseIso8601WeekFormat(isNegative, source);
if (source.contains("-")) return parseIso8601AlternativeFormat(isNegative, source);
return parseIso8601Format(isNegative, source);
}
return parseStandardFormat(isNegative, source);
}
private static Duration parseIso8601Format(boolean isNegative, String source)
{
Matcher matcher = ISO8601_PATTERN.matcher(source);
if (!matcher.matches())
throw new IllegalArgumentException(
String.format("Unable to convert '%s' to a duration", source));
Builder builder = new Builder(isNegative);
if (matcher.group(1) != null) builder.addYears(groupAsLong(matcher, 2));
if (matcher.group(3) != null) builder.addMonths(groupAsLong(matcher, 4));
if (matcher.group(5) != null) builder.addDays(groupAsLong(matcher, 6));
// Checks if the String contains time information
if (matcher.group(7) != null)
{
if (matcher.group(8) != null) builder.addHours(groupAsLong(matcher, 9));
if (matcher.group(10) != null) builder.addMinutes(groupAsLong(matcher, 11));
if (matcher.group(12) != null) builder.addSeconds(groupAsLong(matcher, 13));
}View on GitHub (pinned to 88fd0f6a0e)
Solutions
- Rewrite the value in valid ISO 8601 duration form: order must be P [n]Y [n]M [n]D [T [n]H [n]M [n]S], uppercase designators, no fractions, e.g. 'P1Y2M3DT4H5M6S'.
- Or use the Cassandra standard format instead ('1y2mo3d4h5m6s'), which does not require the P prefix.
- Check unit semantics: months use uppercase M, minutes use the M after T; 'PM' or 'PT' with nothing after T is invalid.
- If the string comes from another library (java.time.Duration, Joda), convert it explicitly before passing it in.
- Wrap the call in try/catch for IllegalArgumentException and surface the offending string to the user.
Example fix
// before
Duration d = Duration.from("PT"); // throws IllegalArgumentException
// after
Duration d = Duration.from("PT1H30M"); // valid ISO 8601: 1.5 hours
// or use the standard format
Duration d2 = Duration.from("1h30m"); Defensive patterns
Strategy: validation
Validate before calling
private static final Pattern ISO8601 = Pattern.compile("P((\\d+)Y)?((\\d+)M)?((\\d+)D)?(T((\\d+)H)?((\\d+)M)?((\\d+)S)?)?");
boolean isParsableIso8601Duration(String s) {
String v = s.startsWith("-") ? s.substring(1) : s;
return v.startsWith("P") && ISO8601.matcher(v).matches() && !v.equals("P") && !v.equals("PT");
} Type guard
boolean isValidDurationString(String s) {
return s != null && !s.isEmpty() && (s.startsWith("-") ? s.length() > 1 : true);
} Try / catch
try {
Duration d = Duration.from(input);
} catch (IllegalArgumentException e) {
LOG.warn("Invalid duration '{}': {}", input, e.getMessage());
throw new ConfigurationException("duration must match P[n]Y[n]M[n]DT[n]H[n]M[n]S, got: " + input);
} Prevention
- Always use uppercase ISO 8601 designators with no fractions when using the P-format.
- Prefer the Cassandra standard format (e.g. '1h30m') for hand-written config; it is harder to misformat than ISO 8601.
- Never pass java.time.Duration.toString() output with negative or fractional components directly; convert first.
- Add a regex pre-check in config loading code that reports the offending key and value.
- Unit-test every user-supplied duration string format your tooling accepts.
When it happens
Trigger: Calling Duration.from() (directly or by binding a CQL duration literal via the driver) with a 'P'-prefixed string that is not a valid ISO 8601 duration, e.g. 'PT' (bare T with no time designators), 'P1H' (hours must come after T), 'P' alone, 'P1.5H' (fractional units unsupported), or lowercase designators like 'p1d'. Also any string starting with P and containing '-' that fails the alternative format falls through here.
Common situations: Copy-pasting durations from java.time.Duration#toString() ('PT24H' works but 'PT-1H' or '0.5S' do not), config files carrying Spring/Joda style durations ('P1D30M' out of order is fine here, but 'PT1m' lowercase fails), users writing 'P' + a Go-style duration, or mixing the alternative format 'P0001-02-03T04:05:06' with a typo that then lands in this parser.
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
Related errors
- Unknown duration symbol '%s'
- Timestamp format must be hh:mm:ss[.fffffffff]
- Durations are not allowed inside sets:
- Durations are not allowed as map keys:
- Invalid or malformed
AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10).
Data as JSON: /api/errors/73b99155e9dacda5.
Report an issue: GitHub.