conductor-oss/conductor · error · TerminateWorkflowException

Task %s failed with status: %s and reason: '%s'

Error message

Task %s failed with status: %s and reason: '%s'

What it means

During decide(), the engine collects permissive-but-non-optional tasks that ended in a terminal, non-successful state. If any exist, it joins their failure reasons and throws a TerminateWorkflowException, terminating the workflow. Despite the 'permissive' name, a permissive task only avoids termination when it is also optional; a permissive+non-optional task that fails terminally still kills the workflow.

Source

Thrown at core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java:311

                            .values()
                            .stream()
                            .filter(
                                    t ->
                                            t.getStatus().isTerminal()
                                                    && !t.getStatus().isSuccessful())
                            .toList();
            if (!permissiveTasksTerminalNonSuccessful.isEmpty()) {
                final String errMsg =
                        permissiveTasksTerminalNonSuccessful.stream()
                                .map(
                                        t ->
                                                String.format(
                                                        "Task %s failed with status: %s and reason: '%s'",
                                                        t.getTaskId(),
                                                        t.getStatus(),
                                                        t.getReasonForIncompletion()))
                                .collect(Collectors.joining(". "));
                throw new TerminateWorkflowException(errMsg);
            }
            outcome.isComplete = true;
        }

        return outcome;
    }

    @VisibleForTesting
    List<TaskModel> filterNextLoopOverTasks(
            List<TaskModel> tasks, TaskModel pendingTask, WorkflowModel workflow) {

        // Update the task reference name and iteration
        tasks.forEach(
                nextTask -> {
                    nextTask.setReferenceTaskName(
                            TaskUtils.appendIteration(
                                    nextTask.getReferenceTaskName(), pendingTask.getIteration()));
                    nextTask.setIteration(pendingTask.getIteration());

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. If the task's failure should be tolerated, also mark the WorkflowTask optional=true.
  2. If the task must succeed, fix the underlying task failure (check the task's reasonForIncompletion in the message).
  3. Increase retryCount or fix the task implementation so it succeeds.
  4. Re-read the failing task's status/reason quoted in the exception to root-cause it.

Example fix

// before
{ "name": "t1", "permissive": true }  // optional defaults false -> still terminates on failure

// after
{ "name": "t1", "permissive": true, "optional": true }  // tolerated failure
Defensive patterns

Strategy: validation

Validate before calling

// At definition time, fail fast on the permissive-without-optional foot-gun.
for (WorkflowTask t : def.getTasks()) {
    if (t.isPermissive() && !t.isOptional()) {
        LOGGER.warn("Task {} is permissive but not optional; failure still terminates the workflow", t.getTaskReferenceName());
    }
}

Try / catch

try {
    deciderService.decide(workflow);
} catch (TerminateWorkflowException e) {
    // inspect quoted task id/status/reason in message
    LOGGER.error("Workflow terminated by permissive task: {}", e.getMessage());
}

Prevention

When it happens

Trigger: A WorkflowTask marked permissive=true and optional=false reaches a terminal status that is not successful (FAILED, TIMED_OUT, CANCELED) after exhausting its attempts.

Common situations: Misunderstanding the permissive flag as 'failure is OK' when optional is false; a task with permissive=true whose upstream dependency fails; retry exhaustion on a permissive task.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/4eea72729b7de850. Report an issue: GitHub.