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

  1. Add the user as a candidate first (taskRuntime.addCandidateUser / addUserCandidateUser) then assign
  2. Assign via candidate group membership so the user qualifies as candidate
  3. Use engine-level taskService.setAssignee(taskId, assignee) when unrestricted assignment is intended
  4. 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

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


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)