Activiti/Activiti · error · IllegalStateException
You cannot assign a task to
Error message
You cannot assign a task to ${assignee} due it is not a candidate for it What it means
When assigning a task, TaskRuntimeImpl.assertAssigneeIsACandidateUser enforces that the new assignee is one of the task's candidate users. The low-level engine allows assigning anyone, but the higher-level TaskRuntime restricts claims/assignments to candidates. If the target user is not a candidate, this IllegalStateException is thrown.
Solutions
- Add the user as a candidate first (taskRuntime.addCandidateUser / addUserCandidateUser) then assign
- Assign via candidate group membership so the user qualifies as candidate
- Use engine-level taskService.setAssignee(taskId, assignee) when unrestricted assignment is intended
- Update the BPMN process definition to include the intended users/groups as candidates
Example fix
// before taskRuntime.assign(new AssignTaskPayload(taskId, "mary")); // mary not a candidate -> throws // after taskRuntime.addCandidateUser(new CandidateUserPayload(taskId, "mary")); taskRuntime.assign(new AssignTaskPayload(taskId, "mary"));
Defensive patterns
Strategy: validation
Validate before calling
List<String> candidates = taskRuntime.userCandidates(taskId); // or identity links
if (!candidates.contains(assignee)) { taskRuntime.addCandidateUser(new CandidateUserPayload(taskId, assignee)); } Try / catch
try { taskRuntime.assign(payload); } catch (IllegalStateException e) { /* add candidate then retry once */ } Prevention
- Model assignment flows as candidate-first
- Keep BPMN candidate users/groups in sync with real org data
- Constrain UI assignee pickers to candidate lists
When it happens
Trigger: taskRuntime.assign(new AssignTaskPayload(taskId, assignee)) where assignee is not present in the task's candidate user list (userCandidates(taskId)); also hit on claim flows where the claiming user was never added as candidate.
Common situations: Trying to hand a task to a colleague who was never added as candidate user; process definitions with candidate-groups only (so individual users aren't 'candidate users' as resolved by userCandidates); UI allowing free-text assignee input.
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
- Task with id: cannot be completed because it cannot be…
- The authenticated user cannot delete the task
- The authenticated user cannot update the task
- There is no authenticated user, we need a user…
- There is no authenticated user, we need a user…
AI-assisted analysis of Activiti/Activiti@56435b1a97 (2026-09-09).
Data as JSON: /api/errors/41ce5bcbaccd6d46.
Report an issue: GitHub.
Appendix: source
Thrown at activiti-core/activiti-api-impl/activiti-api-task-runtime-impl/src/main/java/org/activiti/runtime/api/impl/TaskRuntimeImpl.java:547
taskId +
" for user: " +
authenticatedUserId +
" (with groups: " +
userGroups +
" & with roles: " +
userRoles +
")"
);
}
return taskService.getIdentityLinksForTask(taskId);
}
throw new IllegalStateException("There is no authenticated user, we need a user authenticated to find tasks");
}
private void assertAssigneeIsACandidateUser(String taskId, String assignee) {
List<String> userCandidates = userCandidates(taskId);
if (!userCandidates.contains(assignee)) {
throw new IllegalStateException(
"You cannot assign a task to " + assignee + " due it is not a candidate for it"
);
}
}
private void reassignTask(String taskId, String assignee) {
releaseTask(taskId);
taskService.claim(taskId, assignee);
}
private void releaseTask(String taskId) {
assertCanReleaseTask(taskId);
taskService.unclaim(taskId);
}
private void assertCanReleaseTask(String taskId) {
Task task = task(taskId);
View on GitHub (pinned to 56435b1a97)