flowable/flowable-engine · error · FlowableException
UserTask should not be signalled before complete for +…
Error message
UserTask should not be signalled before complete for + taskEntity
What it means
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.
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
Example fix
// before runtimeService.trigger(executionId); // after Task task = taskService.createTaskQuery().executionId(executionId).singleResult(); taskService.complete(task.getId());
Defensive patterns
Strategy: try-catch
Validate before calling
Task t = taskService.createTaskQuery()
.executionId(executionId).singleResult();
if (t != null) {
throw new IllegalStateException("Complete task " + t.getId() + " instead of signalling execution " + executionId);
} Try / catch
try {
runtimeService.trigger(executionId);
} catch (FlowableException e) {
if (e.getMessage().startsWith("UserTask should not be signalled")) {
Task t = taskService.createTaskQuery().executionId(executionId).singleResult();
if (t != null) taskService.complete(t.getId());
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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).
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- UserTask should not be signalled before complete
- a suspended
- already taking a transition
- Can not activate job
- Can only enable a plan item instance which is in state…
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/fa90afb32f5fb793.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/UserTaskActivityBehavior.java:340
if (formKeyValue != null) {
formKey = formKeyValue.toString();
}
} catch (FlowableException e) {
formKey = beforeContext.getFormKey();
LOGGER.warn("property not found in task formKey expression {}", e.getMessage());
}
task.setFormKey(formKey);
}
}
@Override
public void trigger(DelegateExecution execution, String signalName, Object signalData) {
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration();
List<TaskEntity> taskEntities = processEngineConfiguration.getTaskServiceConfiguration().getTaskService()
.findTasksByExecutionId(execution.getId()); // Should be only one
for (TaskEntity taskEntity : taskEntities) {
if (!taskEntity.isDeleted()) {
throw new FlowableException("UserTask should not be signalled before complete for " + taskEntity);
}
}
leave(execution);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void handleAssignments(TaskService taskService, String assignee, String owner, List<String> candidateUsers,
List<String> candidateGroups, TaskEntity task, ExpressionManager expressionManager, DelegateExecution execution,
ProcessEngineConfigurationImpl processEngineConfiguration) {
if (StringUtils.isNotEmpty(assignee)) {
Object assigneeExpressionValue = expressionManager.createExpression(assignee).getValue(execution);
String assigneeValue = null;
if (assigneeExpressionValue != null) {
assigneeValue = assigneeExpressionValue.toString();
}
View on GitHub (pinned to d6d39ce1c6)