flowable/flowable-engine · warning · FlowableException

Case instance '

Error message

Case instance '

What it means

Flowable CMMN throws this when calling CaseInstanceClaimCmd.execute for a case instance that already has an ASSIGNEE identity link, i.e. it has already been claimed by a user. Claiming is a one-time operation; re-claiming without unclaiming first is rejected to protect the existing assignee.

Solutions

  1. Check whether the case instance is already claimed before calling claim, e.g. via identityLinkService.findIdentityLinksByScopeIdAndType(caseInstanceId, ScopeTypes.CMMN) and looking for an ASSIGNEE link
  2. Catch FlowableException and treat the 'is already claimed' message as an expected business condition, informing the user who holds the claim
  3. If reassignment is intended, unclaim the case instance (remove assignee identity link) before claiming with the new user
  4. Fix duplicate claim submission in the UI by disabling the claim action once a claim succeeds

Example fix

// before
caseInstanceService.claim(caseInstanceId, userId); // throws if already claimed
// after
List<IdentityLink> links = identityLinkService.findIdentityLinksByScopeIdAndType(caseInstanceId, ScopeTypes.CMMN);
boolean claimed = links.stream().anyMatch(l -> IdentityLinkType.ASSIGNEE.equals(l.getType()));
if (!claimed) {
    caseInstanceService.claim(caseInstanceId, userId);
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean alreadyClaimed = cmmnEngineConfig.getIdentityLinkServiceConfiguration().getIdentityLinkService()
    .findIdentityLinksByScopeIdAndType(caseInstanceId, ScopeTypes.CMMN).stream()
    .anyMatch(l -> IdentityLinkType.ASSIGNEE.equals(l.getType()));
if (alreadyClaimed) { /* skip claim or show holder */ }

Try / catch

try {
    caseInstanceService.claim(caseInstanceId, userId);
} catch (FlowableException e) {
    if (e.getMessage() != null && e.getMessage().contains("is already claimed")) {
        // surface friendly message to user
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling CmmnTaskService/cmmnEngine CaseInstanceService.claim(caseInstanceId, userId) on a case instance where findIdentityLinksByScopeIdAndType returns an identity link of type ASSIGNEE.

Common situations: Two users clicking a 'claim' button concurrently; a retry of a claim request after it already succeeded; UI not refreshing claim state after another user claimed; batch job reprocessing the same case instance.

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

Appendix: source

Thrown at modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/CaseInstanceClaimCmd.java:63

        this.caseInstanceId = caseInstanceId;
        this.userId = userId;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        CaseInstanceEntityManager caseInstanceEntityManager = CommandContextUtil.getCaseInstanceEntityManager(commandContext);
        CaseInstanceEntity caseInstanceEntity = caseInstanceEntityManager.findById(caseInstanceId);
        if (caseInstanceEntity == null) {
            throw new FlowableObjectNotFoundException("No case instance found for id = '" + caseInstanceId + "'.", CaseInstance.class);
        }

        CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
        if (userId != null) {
            List<IdentityLinkEntity> identityLinks = cmmnEngineConfiguration.getIdentityLinkServiceConfiguration()
                    .getIdentityLinkService().findIdentityLinksByScopeIdAndType(caseInstanceId, ScopeTypes.CMMN);
            for (IdentityLinkEntity identityLink : identityLinks) {
                if (IdentityLinkType.ASSIGNEE.equals(identityLink.getType())) {
                    throw new FlowableException("Case instance '" + caseInstanceId + "' is already claimed.");
                }
            }

            IdentityLinkUtil.createCaseInstanceIdentityLink(caseInstanceEntity, userId, null, IdentityLinkType.ASSIGNEE, cmmnEngineConfiguration);

            caseInstanceEntityManager.updateCaseInstanceClaimTime(caseInstanceEntity, cmmnEngineConfiguration.getClock().getCurrentTime(), userId);

            if (cmmnEngineConfiguration.getCaseInstanceStateInterceptor() != null) {
                cmmnEngineConfiguration.getCaseInstanceStateInterceptor().handleClaim(caseInstanceEntity, userId);
            }

        } else {
            IdentityLinkUtil.deleteCaseInstanceIdentityLinks(caseInstanceEntity, null, null, IdentityLinkType.ASSIGNEE, cmmnEngineConfiguration);

            caseInstanceEntityManager.updateCaseInstanceClaimTime(caseInstanceEntity, null, null);

            if (cmmnEngineConfiguration.getCaseInstanceStateInterceptor() != null) {
                cmmnEngineConfiguration.getCaseInstanceStateInterceptor().handleUnclaim(caseInstanceEntity, userId);

View on GitHub (pinned to d6d39ce1c6)