apache/iceberg · error · IllegalArgumentException

Cannot parse to a duration string value: %s: %s

Error message

Cannot parse to a duration string value: %s: %s

What it means

JsonUtil.getDurationStringOrNull(property, node) verifies that the given JSON field is a string parseable by java.time.Duration.parse. If the value is present but not a valid ISO-8601 duration, it throws IllegalArgumentException naming the property and offending value.

Source

Thrown at core/src/main/java/org/apache/iceberg/util/JsonUtil.java:201

      return null;
    }
    JsonNode pNode = node.get(property);
    if (pNode != null && pNode.isNull()) {
      return null;
    }
    return getString(property, node);
  }

  public static String getDurationStringOrNull(String property, JsonNode node) {
    String value = getStringOrNull(property, node);
    if (value == null) {
      return null;
    }

    try {
      java.time.Duration.parse(value);
    } catch (RuntimeException e) {
      throw new IllegalArgumentException(
          String.format(
              "Cannot parse to a duration string value: %s: %s", property, node.get(property)),
          e);
    }

    return value;
  }

  public static ByteBuffer getByteBufferOrNull(String property, JsonNode node) {
    if (!node.has(property) || node.get(property).isNull()) {
      return null;
    }

    JsonNode pNode = node.get(property);
    Preconditions.checkArgument(
        pNode.isTextual(), "Cannot parse byte buffer from non-text value: %s: %s", property, pNode);
    return ByteBuffer.wrap(
        BaseEncoding.base16().decode(pNode.textValue().toUpperCase(Locale.ROOT)));

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Change the value to a valid ISO-8601 duration, e.g. 'PT10M' for 10 minutes or 'PT1H30S'
  2. Use java.time.Duration.toString() or Duration.of(...).toString() when generating config JSON
  3. Validate the string with Duration.parse() before writing it into the config

Example fix

// before
{ "history.expire.max-snapshot-age": "10 minutes" }
// after
{ "history.expire.max-snapshot-age": "PT10M" }
Defensive patterns

Strategy: validation

Validate before calling

String v = jsonNode.get("history.expire.max-snapshot-age").asText();
java.time.Duration.parse(v); // throws DateTimeParseException if invalid

Type guard

boolean isIsoDuration(String s) { try { java.time.Duration.parse(s); return true; } catch (Exception e) { return false; } }

Try / catch

try { Duration d = JsonUtil.getDurationStringOrNull(prop, node) ...; } catch (IllegalArgumentException e) { /* fix config value to ISO-8601 */ }

Prevention

When it happens

Trigger: Passing a config field like 'history.expire.max-snapshot-age-ms'-style durations as '5 minutes' or '5' instead of the ISO-8601 form 'PT5M' in JSON table metadata or catalog config.

Common situations: Hand-edited JSON config files using human-readable duration strings; tooling that writes durations without the required PT prefix; timezone/locale-specific formatting leaking into config.

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/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/ea8d9a4183bc64ac. Report an issue: GitHub.