kestra-io/kestra · error · PebbleException

Subflow '%s'.'%s' ended in state %s (execution %s).

Error message

Subflow '%s'.'%s' ended in state %s (execution %s).

What it means

After runAndWait returns a terminal execution, the subflow() function checks the state. Only SUCCESS and WARNING are considered acceptable outcomes. Any other terminal state (FAILED, KILLED, CANCELLED, etc.) causes the function to throw, including the execution ID so the user can look up the failure details in the UI. This is by design: subflow() is a synchronous call and a failed child flow should surface as an error to the caller.

Source

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

                    targetFlow,
                    (f, e) -> flowInputOutput.get().readExecutionInputs(f, e, inputs),
                    labels,
                    Optional.empty()
                );
            } catch (Exception e) {
                throw new PebbleException(e, "Invalid inputs for subflow '" + namespace + "'.'" + id + "': " + e.getMessage(), lineNumber, self.getName());
            }

            Execution terminated;
            try {
                terminated = executionService.get().runAndWait(execution, targetFlow, timeout);
            } catch (Exception e) {
                throw new PebbleException(e, "Failed to run subflow '" + namespace + "'.'" + id + "': " + e.getMessage(), lineNumber, self.getName());
            }

            State.Type state = terminated.getState().getCurrent();
            if (state != State.Type.SUCCESS && state != State.Type.WARNING) {
                throw new PebbleException(
                    null, "Subflow '" + namespace + "'.'" + id + "' ended in state " + state
                        + " (execution " + terminated.getId() + ").",
                    lineNumber, self.getName()
                );
            }

            return Result.of(terminated);
        } finally {
            int current = DEPTH.get() - 1;
            if (current <= 0) {
                DEPTH.remove();
            } else {
                DEPTH.set(current);
            }
        }
    }

    @SuppressWarnings("unchecked")

View on GitHub (pinned to 823fada927)

Solutions

  1. Look up the execution ID from the error message in the Kestra UI to find the root cause of the failure.
  2. Fix the failing task(s) in the target flow.
  3. If the failure is expected or intermittent, consider adding retry logic or error handling in the target flow.
  4. If you want to tolerate failures, consider using the Subflow task type with wait=true and checks outputs.state instead of subflow().

Example fix

# The error includes the execution ID, e.g.:
# "Subflow 'company.team'.'child' ended in state FAILED (execution aBcDeFgHiJ)."
# Look up that execution ID in the Kestra UI to find the failed task and fix it.

# If you need to tolerate subflow failures, use the Subflow task instead:
- id: run_child
  type: io.kestra.plugin.core.flow.Subflow
  namespace: company.team
  flowId: child
  wait: true
  outputs:
    state: "{{ outputs.run_child.executionId | lookupState }}"
# Then check outputs.run_child.state before proceeding
Defensive patterns

Strategy: validation

Validate before calling

# Before relying on subflow(), ensure the target flow is robust:
#   - Test it independently via the Kestra execute form
#   - Add error handling (restart, retry) within the target flow
# If you need to tolerate non-success states, use the Subflow task instead:
# - id: run_child
#   type: io.kestra.plugin.core.flow.Subflow
#   namespace: ns
#   flowId: child
#   wait: true
# Then check {{ outputs.run_child.state }} and branch accordingly.

Prevention

When it happens

Trigger: The target flow runs to completion but ends in FAILED, KILLED, CANCELLED, or another non-success state. A task inside the target flow fails, causing the flow to fail. The target flow is killed manually while the subflow() call is waiting. The target flow has no tasks or has a configuration that results in a non-success terminal state.

Common situations: The target flow has a bug or a failing task. An external dependency (API, database) the target flow depends on is unavailable. The target flow was manually killed by a user. Resource constraints cause a task to fail.

Related errors


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