{"record":{"id":"4eea72729b7de850","repo":"conductor-oss/conductor","slug":"task-s-failed-with-status-s-and-reason-s","errorCode":null,"errorMessage":"Task %s failed with status: %s and reason: '%s'","messagePattern":"Task (.+?) failed with status: (.+?) and reason: '(.+?)'","errorType":"exception","errorClass":"TerminateWorkflowException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java","lineNumber":311,"sourceCode":"                            .values()\n                            .stream()\n                            .filter(\n                                    t ->\n                                            t.getStatus().isTerminal()\n                                                    && !t.getStatus().isSuccessful())\n                            .toList();\n            if (!permissiveTasksTerminalNonSuccessful.isEmpty()) {\n                final String errMsg =\n                        permissiveTasksTerminalNonSuccessful.stream()\n                                .map(\n                                        t ->\n                                                String.format(\n                                                        \"Task %s failed with status: %s and reason: '%s'\",\n                                                        t.getTaskId(),\n                                                        t.getStatus(),\n                                                        t.getReasonForIncompletion()))\n                                .collect(Collectors.joining(\". \"));\n                throw new TerminateWorkflowException(errMsg);\n            }\n            outcome.isComplete = true;\n        }\n\n        return outcome;\n    }\n\n    @VisibleForTesting\n    List<TaskModel> filterNextLoopOverTasks(\n            List<TaskModel> tasks, TaskModel pendingTask, WorkflowModel workflow) {\n\n        // Update the task reference name and iteration\n        tasks.forEach(\n                nextTask -> {\n                    nextTask.setReferenceTaskName(\n                            TaskUtils.appendIteration(\n                                    nextTask.getReferenceTaskName(), pendingTask.getIteration()));\n                    nextTask.setIteration(pendingTask.getIteration());","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/core/src/main/java/com/netflix/conductor/core/execution/DeciderService.java#L293-L329","documentation":"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.","triggerScenarios":"A WorkflowTask marked permissive=true and optional=false reaches a terminal status that is not successful (FAILED, TIMED_OUT, CANCELED) after exhausting its attempts.","commonSituations":"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.","solutions":["If the task's failure should be tolerated, also mark the WorkflowTask optional=true.","If the task must succeed, fix the underlying task failure (check the task's reasonForIncompletion in the message).","Increase retryCount or fix the task implementation so it succeeds.","Re-read the failing task's status/reason quoted in the exception to root-cause it."],"exampleFix":"// before\n{ \"name\": \"t1\", \"permissive\": true }  // optional defaults false -> still terminates on failure\n\n// after\n{ \"name\": \"t1\", \"permissive\": true, \"optional\": true }  // tolerated failure","handlingStrategy":"validation","validationCode":"// At definition time, fail fast on the permissive-without-optional foot-gun.\nfor (WorkflowTask t : def.getTasks()) {\n    if (t.isPermissive() && !t.isOptional()) {\n        LOGGER.warn(\"Task {} is permissive but not optional; failure still terminates the workflow\", t.getTaskReferenceName());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    deciderService.decide(workflow);\n} catch (TerminateWorkflowException e) {\n    // inspect quoted task id/status/reason in message\n    LOGGER.error(\"Workflow terminated by permissive task: {}\", e.getMessage());\n}","preventionTips":["Mark a WorkflowTask optional=true if its failure should be tolerated.","Understand permissive alone does NOT prevent termination.","Audit definitions for permissive+non-optional tasks before deploy."],"tags":["conductor","workflow","decider","permissive-task","terminate-workflow","task-failure"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}