kestra-io/kestra · error · PebbleException
The 'subflow' function can only be used in a flow context (e
Error message
The 'subflow' function can only be used in a flow context (e.g. an input's 'values'); the caller flow could not be resolved.
What it means
The subflow() function reads the 'flow' variable from the Pebble evaluation context to determine the caller flow's tenantId, namespace, and id (used for ACL checks and label propagation). If the 'flow' variable is absent (null), the function cannot resolve the caller flow and refuses to proceed. This is distinct from error 209 (which checks for taskrun/trigger) — here the flow context itself is missing entirely, meaning the function is being rendered in a non-flow context (e.g., a standalone template test, a system-level render).
Source
Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/SubflowFunction.java:122
// The call blocks until the subflow terminates: only allow it where rendering happens on a
// blocking-friendly thread (flow-input rendering on the webserver). A top-level 'taskrun' or
// 'trigger' variable means we are rendering a task or trigger property, which may run on a worker.
if (context.getVariable("taskrun") != null || context.getVariable("trigger") != null) {
throw new PebbleException(
null, "The 'subflow' function can only be used at flow-input render time (e.g. an input's 'values'); it is not supported inside task or trigger properties.", lineNumber,
self.getName()
);
}
String namespace = (String) args.get(NAMESPACE_ARG);
String id = (String) args.get(ID_ARG);
if (namespace == null || id == null) {
throw new PebbleException(null, "The 'subflow' function expects the arguments 'namespace' and 'id'.", lineNumber, self.getName());
}
Map<String, String> flow = (Map<String, String>) context.getVariable("flow");
if (flow == null) {
throw new PebbleException(
null, "The 'subflow' function can only be used in a flow context (e.g. an input's 'values'); the caller flow could not be resolved.", lineNumber, self.getName()
);
}
String tenantId = flow.get("tenantId");
String callerNamespace = flow.get(NAMESPACE_ARG);
String callerId = flow.get("id");
Optional<Integer> revision = Optional.ofNullable(args.get(REVISION_ARG)).map(r -> ((Number) r).intValue());
Map<String, Object> rawInputs = (Map<String, Object>) args.get(INPUTS_ARG);
Map<String, Object> inputs = rawInputs != null ? rawInputs : Map.of();
List<Label> labels = buildLabels(args.get(LABELS_ARG), self, lineNumber);
Duration timeout = resolveTimeout(args.get(TIMEOUT_ARG), self, lineNumber);
int depth = DEPTH.get();
if (depth >= configuration.maxDepth()) {
throw new PebbleException(
null, "The 'subflow' function exceeded the maximum nesting depth of " + configuration.maxDepth()View on GitHub (pinned to 823fada927)
Solutions
- Ensure subflow() is only used inside a flow input's 'values' field where the flow context is fully populated.
- If testing, provide a mock flow variable in the evaluation context.
- Move the subflow() call to the correct render context (flow input values at execute-form time).
Example fix
# The error is contextual, not syntactic. Ensure subflow() is in a flow input values field:
# before — subflow() in a context without flow variable (e.g., test harness)
# (no YAML fix — move the call to a proper flow input)
# after — correct placement
inputs:
- id: choices
type: SELECT
values: "{{ subflow(namespace='company.team', id='option_generator').outputs.options }}" Defensive patterns
Strategy: validation
Validate before calling
# subflow() requires a flow context. Only use it in a flow input's 'values' field.
# There is no Pebble-level guard — the context must be correct by construction.
# Correct placement:
# inputs:
# - id: my_select
# type: SELECT
# values: "{{ subflow(namespace='ns', id='gen').outputs.options }}" Prevention
- Only call subflow() in a flow input's 'values' field where the flow context is guaranteed.
- Do not use subflow() in unit test harnesses or standalone template renders without a flow variable.
- If you encounter this error, verify you are not calling subflow() from a plugin or custom render path.
When it happens
Trigger: Rendering subflow() in a context where no flow variable is injected — e.g., testing a Pebble template outside a flow, rendering in a plugin development harness, or calling it from a non-flow execution context. A misconfigured render pipeline that does not populate the standard flow variables.
Common situations: Unit testing Pebble expressions without a full RunVariables setup. Using subflow() in a custom trigger or plugin that does not provide the standard flow context. Edge cases in the executor where the flow variable is not yet populated.
Related errors
- The 'subflow' function can only be used at flow-input render
- The 'subflow' function expects the arguments 'namespace' and
- The 'subflow' function exceeded the maximum nesting depth of
- Cannot run the invalid flow '%s'.'%s': %s
- Cannot run the disabled flow '%s'.'%s'.
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/e0449400b63f4104.
Report an issue: GitHub.