flowable/flowable-engine · error · FlowableTaskAlreadyClaimedException
Task '' is already claimed by someone else.
Error message
Task '' is already claimed by someone else.
What it means
ClaimTaskCmd claims a task for a user. If the task already has an assignee different from the claiming user, Flowable throws FlowableTaskAlreadyClaimedException; claiming by the same assignee is silently ignored.
Source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/ClaimTaskCmd.java:60
protected Void execute(CommandContext commandContext, TaskEntity task) {
if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, task.getProcessDefinitionId())) {
Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
compatibilityHandler.claimTask(taskId, userId);
return null;
}
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
if (userId != null) {
Clock clock = processEngineConfiguration.getClock();
task.setClaimTime(clock.getCurrentTime());
task.setClaimedBy(userId);
task.setState(Task.CLAIMED);
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 FlowableTaskAlreadyClaimedException(task.getId(), task.getAssignee());
}
CommandContextUtil.getActivityInstanceEntityManager(commandContext).recordTaskInfoChange(task, clock.getCurrentTime());
} else {
TaskHelper.changeTaskAssignee(task, userId);
if (processEngineConfiguration.getUserTaskStateInterceptor() != null) {
processEngineConfiguration.getUserTaskStateInterceptor().handleClaim(task, userId);
}
}
CommandContextUtil.getHistoryManager().createUserIdentityLinkComment(task, userId, IdentityLinkType.ASSIGNEE, true);
} else {
if (task.getAssignee() != null) {
// Task claim time should be null
task.setClaimTime(null);
task.setClaimedBy(null);View on GitHub (pinned to d6d39ce1c6)
Solutions
- Catch FlowableTaskAlreadyClaimedException and refresh the task state / notify the user.
- Use taskService.createTaskQuery().taskId(id).taskAssignee(userId) beforehand to check current assignee.
- Call taskService.unclaim(taskId) first if reassignment is intended (with appropriate authorization).
Example fix
// before
taskService.claim(taskId, userId);
// after
try {
taskService.claim(taskId, userId);
} catch (FlowableTaskAlreadyClaimedException e) {
// task already assigned to e.getCurrentAssignee(); refresh UI
} Defensive patterns
Strategy: try-catch
Validate before calling
Task t = taskService.createTaskQuery().taskId(taskId).singleResult(); boolean claimable = t != null && t.getAssignee() == null;
Try / catch
try {
taskService.claim(taskId, userId);
} catch (FlowableTaskAlreadyClaimedException e) {
// refresh UI, show current assignee e.getCurrentAssignee()
} Prevention
- Always wrap claim in a handler for FlowableTaskAlreadyClaimedException.
- Refresh task lists after claiming to avoid stale assignee info.
- Check current assignee first when the UI allows a claim attempt.
When it happens
Trigger: taskService.claim(taskId, userId) where the task's current assignee is another user; two users claiming concurrently.
Common situations: Collaborative task lists where multiple users click 'claim' at once; retrying a claim after another user already took it; stale UI showing the task as unclaimed.
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 '
- Cannot resume task with id '
- No task associated. Call businessProcess.startTask() first.
- taskId is null
- Cannot find task with id ${taskId}
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/cdbb5ce48db7b4ea.
Report an issue: GitHub.