flowable/flowable-engine · error · FlowableIllegalArgumentException

A process instance id is required, but the provided id '" +

Error message

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."

What it means

ProcessInstanceClaimCmd requires the id of the ROOT execution (the process instance itself). If the id resolves to a child/concurrent execution (isProcessInstanceType() is false), it throws FlowableIllegalArgumentException telling the caller to pass a root execution id instead.

Source

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

    public ProcessInstanceClaimCmd(String processInstanceId, String userId) {
        if (processInstanceId == null || processInstanceId.isEmpty()) {
            throw new FlowableIllegalArgumentException("The process instance id is mandatory, but '" + processInstanceId + "' has been provided.");
        }

        this.processInstanceId = processInstanceId;
        this.userId = userId;
    }

    @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);

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Pass the processInstanceId (execution.getId() where isProcessInstanceType() is true / execution.getProcessInstanceId()) instead of a child execution id.
  2. When iterating executions, filter with executionQuery.processInstanceId(pid) and take the root, or check ProcessInstance type before selecting the id.
  3. Catch FlowableIllegalArgumentException; if the message mentions a parent process instance id, extract it and retry the claim on the root id.
  4. Store and propagate processInstanceId (not executionId) wherever claim/unclaim operations will be needed.

Example fix

// before
Execution exec = runtimeService.createExecutionQuery().executionId(childExecutionId).singleResult();
managementService.executeCommand(new ProcessInstanceClaimCmd(childExecutionId, userId));
// after
Execution exec = runtimeService.createExecutionQuery().executionId(childExecutionId).singleResult();
String rootId = exec.getProcessInstanceId(); // root execution id
managementService.executeCommand(new ProcessInstanceClaimCmd(rootId, userId));
Defensive patterns

Strategy: validation

Validate before calling

Execution exec = runtimeService.createExecutionQuery().executionId(id).singleResult();
if (exec == null || !exec.isProcessInstanceType()) { throw new IllegalArgumentException("A root process instance id is required"); }

Type guard

boolean isRootProcessInstanceId(String id) {
    Execution e = runtimeService.createExecutionQuery().executionId(id).singleResult();
    return e != null && e.isProcessInstanceType();
}

Try / catch

try {
    managementService.executeCommand(new ProcessInstanceClaimCmd(id, userId));
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("child execution")) {
        // retry with the parent process instance id reported in the message
    } else { throw e; }
}

Prevention

When it happens

Trigger: Executing ProcessInstanceClaimCmd with an execution id obtained from ExecutionQuery results for a child scope (e.g. a concurrent or event-scope execution) rather than the process instance id.

Common situations: Iterating RuntimeService.createExecutionQuery() results and using each execution's id; confusing executionId with processInstanceId in process variable/callback payloads; nested/async-continuation processes producing extra child executions.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/b3081f71dc90fc09. Report an issue: GitHub.