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 string (e.g. 'PT30S').
What it means
The `subflow()` function's `timeout` argument must be either null (use default), a `Duration` object, or a `String`. If it is any other type — a Number, Boolean, Map, or List — this `PebbleException` is thrown because no branch in `resolveTimeout` can handle it. Unlike error 220 (which is about an unparseable string), this is about a fundamentally wrong type.
Source
Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/SubflowFunction.java:248
// 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}
* @param labels the execution labels as a {@code key -> value} map
*/
public record Result(String id, String state, Map<String, Object> outputs, Map<String, String> labels) {View on GitHub (pinned to 823fada927)
Solutions
- Pass the timeout as an ISO-8601 duration string: `timeout='PT30S'`.
- Omit `timeout` to use the default.
- If the value comes from a variable, coerce it to a properly formatted duration string before passing it.
Example fix
# before
values: "{{ subflow(namespace='company.team', id='my_flow', timeout=30) }}"
# after
values: "{{ subflow(namespace='company.team', id='my_flow', timeout='PT30S') }}" Defensive patterns
Strategy: validation
Validate before calling
// Ensure timeout is null, a Duration, or a String before calling subflow()
public static String validateTimeoutArg(Object timeout) {
if (timeout == null) return null; // use default
if (timeout instanceof Duration || timeout instanceof String) {
return timeout.toString();
}
throw new IllegalArgumentException("timeout must be a String or Duration, got: " + timeout.getClass());
} Type guard
const isValidTimeoutType = (v: unknown): boolean => v === null || typeof v === 'string';
Prevention
- Never pass a numeric literal as timeout; always use a quoted ISO-8601 string.
- If a numeric input must be used, convert it: `timeout='PT' ~ inputs.seconds ~ 'S'`.
When it happens
Trigger: Passing `timeout=30` (a numeric literal in the template) or `timeout=true` to `subflow()`. Passing a variable that resolves to a non-string, non-Duration object at render time.
Common situations: A template author writes `timeout=30` expecting it to mean 30 seconds, but Pebble evaluates `30` as an Integer. A flow input passes a numeric input value directly to `timeout`.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- The 'subflow' function 'timeout' must be an ISO-8601 duratio
- The 'subflow' function expects the arguments 'namespace' and
- Invalid inputs for subflow '%s'.'%s': %s
- Failed to run subflow '%s'.'%s': %s
- The 'subflow' function 'labels' must be a map of string keys
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/06775f478e458ad0.
Report an issue: GitHub.