{"record":{"id":"a5e31f21bc591260","repo":"flowable/flowable-engine","slug":"usertask-should-not-be-signalled-before-complete","errorCode":null,"errorMessage":"UserTask should not be signalled before complete","messagePattern":"UserTask should not be signalled before complete","errorType":"exception","errorClass":"ActivitiException","httpStatus":null,"severity":"error","filePath":"modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java","lineNumber":234,"sourceCode":"            task.fireEvent(TaskListener.EVENTNAME_CREATE);\n\n            // All properties set, now firing 'create' events\n            if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {\n                Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(\n                        ActivitiEventBuilder.createEntityEvent(FlowableEngineEventType.TASK_CREATED, task),\n                        EngineConfigurationConstants.KEY_PROCESS_ENGINE_CONFIG);\n            }\n        }\n\n        if (skipUserTask) {\n            task.complete(null, false, false);\n        }\n    }\n\n    @Override\n    public void signal(ActivityExecution execution, String signalName, Object signalData) throws Exception {\n        if (!((ExecutionEntity) execution).getTasks().isEmpty())\n            throw new ActivitiException(\"UserTask should not be signalled before complete\");\n        leave(execution);\n    }\n\n    @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n    protected void handleAssignments(Expression assigneeExpression, Expression ownerExpression, Set<Expression> candidateUserExpressions,\n                                     Set<Expression> candidateGroupExpressions, TaskEntity task, ActivityExecution execution) {\n\n        if (assigneeExpression != null) {\n            Object assigneeExpressionValue = assigneeExpression.getValue(execution);\n            String assigneeValue = null;\n            if (assigneeExpressionValue != null) {\n                assigneeValue = assigneeExpressionValue.toString();\n            }\n            task.setAssignee(assigneeValue, true, false);\n        }\n\n        if (ownerExpression != null) {\n            Object ownerExpressionValue = ownerExpression.getValue(execution);","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java#L216-L252","documentation":"Thrown in UserTaskActivityBehavior.signal() when a signal is delivered to an execution that still has active tasks. A user task must be completed (via TaskService.completeTask), which removes the task and leaves the activity; signalling the activity directly before completion is an invalid state transition and always a bug in calling code.","triggerScenarios":"Calling execution.signal(...) / RuntimeService.trigger(executionId) on the execution of an active user task whose tasks list is non-empty, instead of completing the task first.","commonSituations":"Custom code that drives the process with signal/trigger instead of TaskService; generic activity-completion wrappers that trigger executions without checking the current activity type; migration of code written for receive tasks to user tasks.","solutions":["Complete the user task via TaskService.complete(taskId) instead of triggering/signalling the execution","Before triggering, check the current activity type and route user tasks through the task-completion path","Query the pending task (TaskService.createTaskQuery().executionId(executionId)) and complete it programmatically","Remove erroneous signal/trigger calls on user-task executions in custom process-interaction code"],"exampleFix":"// before\nruntimeService.trigger(executionId);\n// after\nTask task = taskService.createTaskQuery().executionId(executionId).singleResult();\ntaskService.complete(task.getId());","handlingStrategy":"validation","validationCode":"Task pending = taskService.createTaskQuery().executionId(executionId).singleResult();\nif (pending != null) {\n    taskService.complete(pending.getId());\n} else {\n    runtimeService.trigger(executionId);\n}","typeGuard":null,"tryCatchPattern":"try {\n    runtimeService.trigger(executionId);\n} catch (ActivitiException e) {\n    if (\"UserTask should not be signalled before complete\".equals(e.getMessage())) {\n        // fall back to completing the pending task\n    }\n}","preventionTips":["Always complete user tasks via TaskService.complete, never via execution signal","Check current activity type before triggering an execution","Centralize execution triggering in one utility that handles user tasks specially"],"tags":["user-task","invalid-state","signal"],"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"}