{"record":{"id":"fa90afb32f5fb793","repo":"flowable/flowable-engine","slug":"usertask-should-not-be-signalled-before-complete-f","errorCode":null,"errorMessage":"UserTask should not be signalled before complete for + taskEntity","messagePattern":"UserTask should not be signalled before complete for \\+ taskEntity","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java","lineNumber":340,"sourceCode":"                if (formKeyValue != null) {\n                    formKey = formKeyValue.toString();\n                }\n            } catch (FlowableException e) {\n                formKey = beforeContext.getFormKey();\n                LOGGER.warn(\"property not found in task formKey expression {}\", e.getMessage());\n            }\n            task.setFormKey(formKey);\n        }\n    }\n\n    @Override\n    public void trigger(DelegateExecution execution, String signalName, Object signalData) {\n        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();\n        List<TaskEntity> taskEntities = processEngineConfiguration.getTaskServiceConfiguration().getTaskService()\n                .findTasksByExecutionId(execution.getId()); // Should be only one\n        for (TaskEntity taskEntity : taskEntities) {\n            if (!taskEntity.isDeleted()) {\n                throw new FlowableException(\"UserTask should not be signalled before complete for \" + taskEntity);\n            }\n        }\n\n        leave(execution);\n    }\n\n    @SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n    protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers,\n            List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution, \n            ProcessEngineConfigurationImpl processEngineConfiguration) {\n\n        if (StringUtils.isNotEmpty(assignee)) {\n            Object assigneeExpressionValue = expressionManager.createExpression(assignee).getValue(execution);\n            String assigneeValue = null;\n            if (assigneeExpressionValue != null) {\n                assigneeValue = assigneeExpressionValue.toString();\n            }\n","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java#L322-L358","documentation":"UserTaskActivityBehavior.trigger is invoked when a signal/trigger arrives at the waiting user-task execution. If the execution still has a non-deleted task, the task was never completed, and signalling it is illegal; the engine throws this FlowableException. Only after the task is completed (deleted/moved on) should the execution be left.","triggerScenarios":"Calling RuntimeService.trigger(executionId) (or signal) on an execution currently sitting at a user task whose task is still active; boundary-event/boundary logic or custom code signalling the execution directly instead of completing the TaskService task.","commonSituations":"Custom timeout or escalation logic calling taskService-completion versus execution.trigger incorrectly; boundary events wiring mistakes; scripts that signal the process instead of calling TaskService.complete(taskId).","solutions":["Complete the task instead: call TaskService.complete(taskId) rather than runtimeService.trigger(executionId)","Check taskEntity.isDeleted()/task state; only signal once the task no longer exists for that execution","If using a timeout, attach a boundary timer event to the user task instead of signalling the execution manually","Verify custom code that auto-completes tasks marks/deletes them before leaving the execution"],"exampleFix":"// before\nruntimeService.trigger(executionId);\n// after\nTask task = taskService.createTaskQuery().executionId(executionId).singleResult();\ntaskService.complete(task.getId());","handlingStrategy":"try-catch","validationCode":"Task t = taskService.createTaskQuery()\n    .executionId(executionId).singleResult();\nif (t != null) {\n    throw new IllegalStateException(\"Complete task \" + t.getId() + \" instead of signalling execution \" + executionId);\n}","typeGuard":null,"tryCatchPattern":"try {\n    runtimeService.trigger(executionId);\n} catch (FlowableException e) {\n    if (e.getMessage().startsWith(\"UserTask should not be signalled\")) {\n        Task t = taskService.createTaskQuery().executionId(executionId).singleResult();\n        if (t != null) taskService.complete(t.getId());\n    }\n}","preventionTips":["Always complete tasks via TaskService.complete, never signal the execution","Use boundary timer/message events for timeouts instead of manual signals","Check for an active task before triggering an execution","Document that user-task executions are not signalable until the task is done"],"tags":["user-task","signal","invalid-state","task-service"],"backgroundTag":"invalid-state-transition","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}