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
- Catch ActivitiTaskAlreadyClaimedException and refresh the task state in the UI
- Check task.getAssignee() before claiming and handle the already-claimed case
- 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
- Refresh task state before claiming in UIs
- Treat claims as optimistic-lock style operations
- Use task listeners/optimistic locking to resolve races
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
- Process instance '" + processInstanceId + "' is already…
- Task '
- Task '' is already claimed by someone else.
- A delegated task cannot be completed, but should be…
- A delegated cannot be completed, but should be resolved…
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)