flowable/flowable-engine · error · FlowableException

Process instance '" + processInstanceId + "' is already clai

Error message

Process instance '" + processInstanceId + "' is already claimed."

What it means

Flowable throws this from ProcessInstanceClaimCmd.execute() when a user attempts to claim a process instance that already has an ASSIGNEE identity link. Claiming is exclusive: only one assignee can own a process instance at a time, so the command first loads all identity links for the instance and rejects the claim if an ASSIGNEE link already exists.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/ProcessInstanceClaimCmd.java:70

    @Override
    public Void execute(CommandContext commandContext) {
        ExecutionEntityManager executionManager = CommandContextUtil.getExecutionEntityManager(commandContext);
        ExecutionEntity processInstance = executionManager.findById(processInstanceId);
        if (processInstance == null) {
            throw new FlowableObjectNotFoundException("No process instance found for id = '" + processInstanceId + "'.", ProcessInstance.class);

        } else if (!processInstance.isProcessInstanceType()) {
            throw new FlowableIllegalArgumentException("A process instance id is required, but the provided id " + "'" + processInstanceId + "' " + "points to a child execution of process instance " + "'"
                    + processInstance.getProcessInstanceId() + "'. " + "Please invoke the " + getClass().getSimpleName() + " with a root execution id.");
        }

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        if (userId != null) {
            List<IdentityLinkEntity> identityLinks = processEngineConfiguration.getIdentityLinkServiceConfiguration()
                    .getIdentityLinkService().findIdentityLinksByProcessInstanceId(processInstanceId);
            for (IdentityLinkEntity identityLink : identityLinks) {
                if (IdentityLinkType.ASSIGNEE.equals(identityLink.getType())) {
                    throw new FlowableException("Process instance '" + processInstanceId + "' is already claimed.");
                }
            }

            IdentityLinkUtil.createProcessInstanceIdentityLink(processInstance, userId, null, IdentityLinkType.ASSIGNEE);

            executionManager.updateProcessInstanceClaimTime(processInstance,
                    processEngineConfiguration.getClock().getCurrentTime(), userId);

            if (processEngineConfiguration.getProcessInstanceStateInterceptor() != null) {
                processEngineConfiguration.getProcessInstanceStateInterceptor().handleClaim(processInstance, userId);
            }

        } else {
            IdentityLinkUtil.deleteProcessInstanceIdentityLinks(processInstance, null, null, IdentityLinkType.ASSIGNEE);

            executionManager.updateProcessInstanceClaimTime(processInstance, null, null);

            if (processEngineConfiguration.getProcessInstanceStateInterceptor() != null) {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Check existing claim state before claiming: query identity links via IdentityLinkService.findIdentityLinksByProcessInstanceId and skip or route to the current assignee.
  2. Release/unclaim the existing assignee first (delete the ASSIGNEE identity link) then re-claim with the new user.
  3. Catch FlowableException in the claim path and treat it as a concurrency signal (reload state, notify caller the instance is taken).
  4. Use optimistic UI refresh / retry with backoff when multiple users claim concurrently.

Example fix

// before
managementService.claimProcessInstance(processInstanceId, userId);
// after
List<IdentityLink> links = managementService.getIdentityLinkService()
    .findIdentityLinksByProcessInstanceId(processInstanceId);
boolean claimed = links.stream()
    .anyMatch(l -> IdentityLinkType.ASSIGNEE.equals(l.getType()));
if (!claimed) {
    managementService.claimProcessInstance(processInstanceId, userId);
} else {
    // instance already claimed by another user - handle business case
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean isClaimed(String processInstanceId) {
    return runtimeService.createProcessInstanceQuery()
        .processInstanceId(processInstanceId)
        .variableValueEquals("claimState", "claimed")
        .count() > 0; // or inspect identity links via IdentityLinkService
}

Try / catch

try {
    managementService.claimProcessInstance(processInstanceId, userId);
} catch (FlowableException e) {
    if (e.getMessage() != null && e.getMessage().contains("is already claimed")) {
        // reload state, notify user instance is taken by another assignee
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling managementService.claimProcessInstance(processInstanceId, userId) (via ProcessInstanceClaimCmd) while another user is already recorded as ASSIGNEE for that process instance.

Common situations: Two agents racing to claim the same customer case from a work queue; a stale UI that does not refresh claim state; re-claiming an instance after a previous claim was never released.

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/df2bedd914a56651. Report an issue: GitHub.