flowable/flowable-engine · warning · ActivitiTaskAlreadyClaimedException

Task ' ' is already claimed by someone else.

Error message

Task '${taskId}' is already claimed by someone else.

What it means

ActivitiTaskAlreadyClaimedException is thrown by ClaimTaskCmd when the task already has an assignee different from the user attempting to claim it. The engine enforces single-claim semantics: if the same user re-claims, it silently succeeds; another user gets this exception.

Solutions

  1. Catch ActivitiTaskAlreadyClaimedException and refresh the task state in the UI
  2. Check task.getAssignee() before claiming and handle the already-claimed case
  3. Unclaim (setAssignee(taskId, null)) first if your workflow intentionally allows reassignment

Example fix

// before
taskService.claim(taskId, userId);
// after
try {
    taskService.claim(taskId, userId);
} catch (ActivitiTaskAlreadyClaimedException e) {
    String currentAssignee = taskService.createTaskQuery().taskId(taskId).singleResult().getAssignee();
    // notify user the task is held by currentAssignee
}
Defensive patterns

Strategy: try-catch

Validate before calling

Task t = taskService.createTaskQuery().taskId(taskId).singleResult();
if (t != null && t.getAssignee() != null && !userId.equals(t.getAssignee())) { /* already claimed elsewhere */ }

Type guard

boolean claimable(Task t, String userId) { return t != null && (t.getAssignee() == null || t.getAssignee().equals(userId)); }

Try / catch

try { taskService.claim(taskId, userId); } catch (ActivitiTaskAlreadyClaimedException e) { /* refresh UI, show current assignee */ }

Prevention

When it happens

Trigger: taskService.claim(taskId, userId) where task.getAssignee() is a different, non-null user.

Common situations: Two agents in a tasklist UI claiming simultaneously; a stale UI screen showing the task as unassigned; automation racing a human claimer.

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 flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/06948a3ce062b091. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cmd/ClaimTaskCmd.java:41

    private static final long serialVersionUID = 1L;

    protected String userId;

    public ClaimTaskCmd(String taskId, String userId) {
        super(taskId);
        this.userId = userId;
    }

    @Override
    protected Void execute(CommandContext commandContext, TaskEntity task) {

        if (userId != null) {
            if (task.getAssignee() != null) {
                if (!task.getAssignee().equals(userId)) {
                    // When the task is already claimed by another user, throw exception. Otherwise, ignore
                    // this, post-conditions of method already met.
                    throw new ActivitiTaskAlreadyClaimedException(task.getId(), task.getAssignee());
                }
            } else {
                task.setAssignee(userId, true, true);
            }
        } else {
            // Task should be assigned to no one
            task.setAssignee(null, true, true);
        }

        // Add claim time
        commandContext.getHistoryManager().recordTaskClaim(taskId);

        return null;
    }

    @Override
    protected String getSuspendedTaskException() {
        return "Cannot claim a suspended task";

View on GitHub (pinned to d6d39ce1c6)