kestra-io/kestra · error · PebbleException

The 'subflow' function 'timeout' must be an ISO-8601 duratio

Error message

The 'subflow' function 'timeout' must be an ISO-8601 duration (e.g. 'PT30S'), got '%s'.

What it means

The Pebble `subflow()` function accepts a `timeout` argument that must be an ISO-8601 duration string (e.g. 'PT30S', 'PT5M'). When the value is a String that Java's `Duration.parse()` cannot parse, the underlying `DateTimeParseException` is wrapped and re-thrown as a `PebbleException`. This is a template-level validation error surfaced during flow-input rendering.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/SubflowFunction.java:245

                }
            });
        }
        // tag the execution as run by the subflow() function (cf. the Subflow task's system.from label)
        labels.add(new Label(Label.FROM, Label.FromLabel.SUBFLOW.value));
        return labels;
    }

    private Duration resolveTimeout(Object rawTimeout, PebbleTemplate self, int lineNumber) {
        Duration timeout;
        if (rawTimeout == null) {
            timeout = configuration.defaultTimeout();
        } else if (rawTimeout instanceof Duration d) {
            timeout = d;
        } else if (rawTimeout instanceof String s) {
            try {
                timeout = Duration.parse(s);
            } catch (DateTimeParseException e) {
                throw new PebbleException(e, "The 'subflow' function 'timeout' must be an ISO-8601 duration (e.g. 'PT30S'), got '" + s + "'.", lineNumber, self.getName());
            }
        } else {
            throw new PebbleException(null, "The 'subflow' function 'timeout' must be an ISO-8601 duration string (e.g. 'PT30S').", lineNumber, self.getName());
        }

        if (timeout.compareTo(configuration.maxTimeout()) > 0) {
            throw new PebbleException(null, "The 'subflow' function 'timeout' (" + timeout + ") exceeds the maximum allowed (" + configuration.maxTimeout() + ").", lineNumber, self.getName());
        }
        return timeout;
    }

    /**
     * The minimal, navigable result returned to the template, instead of the full {@link Execution}
     * object (which exposes internal state callers should not depend on).
     *
     * @param id the terminal execution id
     * @param state the terminal state name (e.g. {@code SUCCESS})
     * @param outputs the subflow's flow-level outputs, navigable as {@code subflow(...).outputs.xxx}

View on GitHub (pinned to 823fada927)

Solutions

  1. Change the timeout string to ISO-8601 duration format: 'PT30S' for 30 seconds, 'PT5M' for 5 minutes, 'PT1H' for 1 hour.
  2. Omit the `timeout` argument entirely to use the configured default timeout (`kestra.plugins.subflow.default-timeout`).
  3. Verify the value with an ISO-8601 duration validator before passing it to `subflow()`.

Example fix

# before
values: "{{ subflow(namespace='company.team', id='my_flow', timeout='30s') }}"
# after
values: "{{ subflow(namespace='company.team', id='my_flow', timeout='PT30S') }}"
Defensive patterns

Strategy: validation

Validate before calling

// Validate ISO-8601 duration before passing to subflow()
import java.time.Duration;
import java.time.format.DateTimeParseException;

public static boolean isValidIsoDuration(String value) {
    if (value == null || value.isBlank()) return true; // null = use default
    try {
        Duration.parse(value);
        return true;
    } catch (DateTimeParseException e) {
        return false;
    }
}

Type guard

const isIsoDuration = (s: string): boolean => /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/.test(s);

Prevention

When it happens

Trigger: Calling `subflow(namespace='x', id='y', timeout='30s')` with a non-ISO-8601 format string (e.g. '30s', '5 minutes', '1h'). Any value that does not start with 'P' followed by the standard duration grammar triggers it.

Common situations: Developers coming from cron-like or human-readable duration syntax expect '30s' or '5m' to work. Copy-pasting a timeout value from a task's `timeout` property (which in some Kestra contexts accepts different formats) into the `subflow()` call.

Understand the failure class

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/32ec29c445dc204f. Report an issue: GitHub.