{"record":{"id":"17afa7f2ea038cf7","repo":"ben-manes/caffeine","slug":"key-s-invalid-format-was-s-but-the-duration-ca","errorCode":null,"errorMessage":"key %s invalid format; was %s, but the duration cannot be parsed","messagePattern":"key (.+?) invalid format; was (.+?), but the duration cannot be parsed","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"caffeine/src/main/java/com/github/benmanes/caffeine/cache/CaffeineSpec.java","lineNumber":322,"sourceCode":"  static Duration parseDuration(String key, @Nullable String value) {\n    requireArgument((value != null) && !value.isEmpty(), \"value of key %s omitted\", key);\n    requireNonNull(value);\n\n    boolean isIsoFormat = value.contains(\"p\") || value.contains(\"P\");\n    Duration duration = isIsoFormat\n        ? parseIsoDuration(key, value)\n        : parseSimpleDuration(key, value);\n    requireArgument(!duration.isNegative(),\n        \"key %s invalid format; was %s, but the duration cannot be negative\", key, value);\n    return duration;\n  }\n\n  /** Returns a parsed duration using the ISO-8601 format. */\n  static Duration parseIsoDuration(String key, String value) {\n    try {\n      return Duration.parse(value);\n    } catch (DateTimeParseException e) {\n      throw new IllegalArgumentException(String.format(US,\n          \"key %s invalid format; was %s, but the duration cannot be parsed\", key, value), e);\n    }\n  }\n\n  /** Returns a parsed duration using the simple time unit format. */\n  static Duration parseSimpleDuration(String key, String value) {\n    long duration = parseLong(key, value.substring(0, value.length() - 1));\n    TimeUnit unit = parseTimeUnit(key, value);\n    return Duration.ofNanos(unit.toNanos(duration));\n  }\n\n  /** Returns a parsed {@link TimeUnit} value. */\n  @SuppressWarnings({\"ConstantValue\", \"StatementSwitchToExpressionSwitch\"})\n  static TimeUnit parseTimeUnit(String key, String value) {\n    requireArgument((value != null) && !value.isEmpty(), \"value of key %s omitted\", key);\n    @SuppressWarnings(\"null\")\n    char lastChar = Character.toLowerCase(value.charAt(value.length() - 1));\n    switch (lastChar) {","sourceCodeStart":304,"sourceCodeEnd":340,"githubUrl":"https://github.com/ben-manes/caffeine/blob/9da6581ee366aa63c51e0dc96692d02f9c29ccff/caffeine/src/main/java/com/github/benmanes/caffeine/cache/CaffeineSpec.java#L304-L340","documentation":"When a duration option value contains 'T' (ISO-8601 marker), CaffeineSpec.parseIsoDuration delegates to Duration.parse and throws IllegalArgumentException(\"key %s invalid format; was %s, but the duration cannot be parsed\") on DateTimeParseException. The spec string's duration must be a valid ISO-8601 duration (e.g. PT10M) or a simple form like 10m.","triggerScenarios":"Caffeine.from(\"expireAfterWrite=PT\") or \"expireAfterWrite=P1D30M\" (malformed ISO-8601); using ISO syntax without the T for time components, e.g. \"P10M\" intended as ten minutes (that is ten months in ISO); partial values like \"expireAfterAccess=T30s\".","commonSituations":"Mixing simple-format habits into ISO-8601 strings; generating spec strings from templates that drop required components; assuming 'P10M' means minutes rather than months.","solutions":["Use a complete ISO-8601 duration: expireAfterWrite=PT10M30S (the T separates date from time parts)","Or use the simple format with a unit suffix: expireAfterWrite=10m","When generating values programmatically, build them from Duration.toString() to guarantee validity"],"exampleFix":"# before\nCaffeine.from(\"expireAfterWrite=P10M\")  // parsed as 10 months, or rejected if truncated\nCaffeine.from(\"expireAfterWrite=PT\")    // IllegalArgumentException: cannot be parsed\n\n# after\nCaffeine.from(\"expireAfterWrite=PT10M\") # 10 minutes\n# or\nCaffeine.from(\"expireAfterWrite=10m\")","handlingStrategy":"validation","validationCode":"// Validate ISO durations before building the spec:\nstatic String isoDuration(String key, Duration d) {\n  if (d.isNegative()) throw new ConfigException(key + \" must not be negative\");\n  return key + \"=\" + d; // Duration.toString() is always valid ISO-8601\n}\nString spec = isoDuration(\"expireAfterWrite\", Duration.ofMinutes(10));","typeGuard":null,"tryCatchPattern":"try {\n  return Caffeine.from(spec);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"duration cannot be parsed\")) {\n    // Fall back to builder API with a safe default\n    return Caffeine.newBuilder().expireAfterWrite(Duration.ofMinutes(10));\n  }\n  throw e;\n}","preventionTips":["Remember ISO-8601 'M' after P is months, after T is minutes: PT10M = 10 minutes, P10M = 10 months","Generate duration values from Duration.toString() rather than hand-building strings","Negative durations are rejected separately — always supply non-negative durations"],"tags":["caffeine","configuration","caffeine-spec","duration","iso-8601"],"backgroundTag":null,"analyzedSha":"9da6581ee366aa63c51e0dc96692d02f9c29ccff","analyzedAt":"2026-08-14T14:45:28.147Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}