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

  1. 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'.
  2. Or use the Cassandra standard format instead ('1y2mo3d4h5m6s'), which does not require the P prefix.
  3. Check unit semantics: months use uppercase M, minutes use the M after T; 'PM' or 'PT' with nothing after T is invalid.
  4. If the string comes from another library (java.time.Duration, Joda), convert it explicitly before passing it in.
  5. 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

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


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