{"record":{"id":"32ec29c445dc204f","repo":"kestra-io/kestra","slug":"the-subflow-function-timeout-must-be-an-iso-86","errorCode":null,"errorMessage":"The 'subflow' function 'timeout' must be an ISO-8601 duration (e.g. 'PT30S'), got '%s'.","messagePattern":"The 'subflow' function 'timeout' must be an ISO-8601 duration \\(e\\.g\\. 'PT30S'\\), got '(.+?)'\\.","errorType":"exception","errorClass":"PebbleException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/kestra/core/runners/pebble/functions/SubflowFunction.java","lineNumber":245,"sourceCode":"                }\n            });\n        }\n        // tag the execution as run by the subflow() function (cf. the Subflow task's system.from label)\n        labels.add(new Label(Label.FROM, Label.FromLabel.SUBFLOW.value));\n        return labels;\n    }\n\n    private Duration resolveTimeout(Object rawTimeout, PebbleTemplate self, int lineNumber) {\n        Duration timeout;\n        if (rawTimeout == null) {\n            timeout = configuration.defaultTimeout();\n        } else if (rawTimeout instanceof Duration d) {\n            timeout = d;\n        } else if (rawTimeout instanceof String s) {\n            try {\n                timeout = Duration.parse(s);\n            } catch (DateTimeParseException e) {\n                throw new PebbleException(e, \"The 'subflow' function 'timeout' must be an ISO-8601 duration (e.g. 'PT30S'), got '\" + s + \"'.\", lineNumber, self.getName());\n            }\n        } else {\n            throw new PebbleException(null, \"The 'subflow' function 'timeout' must be an ISO-8601 duration string (e.g. 'PT30S').\", lineNumber, self.getName());\n        }\n\n        if (timeout.compareTo(configuration.maxTimeout()) > 0) {\n            throw new PebbleException(null, \"The 'subflow' function 'timeout' (\" + timeout + \") exceeds the maximum allowed (\" + configuration.maxTimeout() + \").\", lineNumber, self.getName());\n        }\n        return timeout;\n    }\n\n    /**\n     * The minimal, navigable result returned to the template, instead of the full {@link Execution}\n     * object (which exposes internal state callers should not depend on).\n     *\n     * @param id the terminal execution id\n     * @param state the terminal state name (e.g. {@code SUCCESS})\n     * @param outputs the subflow's flow-level outputs, navigable as {@code subflow(...).outputs.xxx}","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/kestra-io/kestra/blob/823fada9274c4f9c251ea0a516460a4f7d958032/core/src/main/java/io/kestra/core/runners/pebble/functions/SubflowFunction.java#L227-L263","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change the timeout string to ISO-8601 duration format: 'PT30S' for 30 seconds, 'PT5M' for 5 minutes, 'PT1H' for 1 hour.","Omit the `timeout` argument entirely to use the configured default timeout (`kestra.plugins.subflow.default-timeout`).","Verify the value with an ISO-8601 duration validator before passing it to `subflow()`."],"exampleFix":"# before\nvalues: \"{{ subflow(namespace='company.team', id='my_flow', timeout='30s') }}\"\n# after\nvalues: \"{{ subflow(namespace='company.team', id='my_flow', timeout='PT30S') }}\"","handlingStrategy":"validation","validationCode":"// Validate ISO-8601 duration before passing to subflow()\nimport java.time.Duration;\nimport java.time.format.DateTimeParseException;\n\npublic static boolean isValidIsoDuration(String value) {\n    if (value == null || value.isBlank()) return true; // null = use default\n    try {\n        Duration.parse(value);\n        return true;\n    } catch (DateTimeParseException e) {\n        return false;\n    }\n}","typeGuard":"const isIsoDuration = (s: string): boolean => /^P(?!$)(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+S)?)?$/.test(s);","tryCatchPattern":null,"preventionTips":["Always use ISO-8601 duration format (starts with 'P', e.g. PT30S, PT5M, PT1H).","Document the format requirement prominently in flow comments.","If the timeout is configurable via a flow input, validate it before passing to subflow()."],"tags":["pebble","subflow","timeout","iso-8601","validation"],"backgroundTag":null,"analyzedSha":"823fada9274c4f9c251ea0a516460a4f7d958032","analyzedAt":"2026-08-14T06:15:17.947Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}