flowable/flowable-engine · error · ActivitiException

Cannot set suspension state for execution '${executionId}':

Error message

Cannot set suspension state for execution '${executionId}': not a process instance.

What it means

AbstractSetProcessInstanceStateCmd.execute requires the found execution to actually be a process instance (root execution). If the id resolves to a child/concurrent execution, changing the process instance suspension state on it is rejected with an ActivitiException.

Source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cmd/AbstractSetProcessInstanceStateCmd.java:57

    public AbstractSetProcessInstanceStateCmd(String executionId) {
        this.executionId = executionId;
    }

    @Override
    public Void execute(CommandContext commandContext) {

        if (executionId == null) {
            throw new ActivitiIllegalArgumentException("ProcessInstanceId cannot be null.");
        }

        ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findExecutionById(executionId);

        if (executionEntity == null) {
            throw new ActivitiObjectNotFoundException("Cannot find processInstance for id '" + executionId + "'.", Execution.class);
        }
        if (!executionEntity.isProcessInstanceType()) {
            throw new ActivitiException("Cannot set suspension state for execution '" + executionId + "': not a process instance.");
        }

        SuspensionStateUtil.setSuspensionState(executionEntity, getNewState());

        // All child executions are suspended
        List<ExecutionEntity> childExecutions = commandContext.getExecutionEntityManager().findChildExecutionsByProcessInstanceId(executionId);
        for (ExecutionEntity childExecution : childExecutions) {
            if (!childExecution.getId().equals(executionId)) {
                SuspensionStateUtil.setSuspensionState(childExecution, getNewState());
            }
        }

        // All tasks are suspended
        List<TaskEntity> tasks = commandContext.getTaskEntityManager().findTasksByProcessInstanceId(executionId);
        for (TaskEntity taskEntity : tasks) {
            SuspensionStateUtil.setSuspensionState(taskEntity, getNewState());
        }

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Use the processInstanceId: for any execution, executionEntity.getProcessInstanceId() gives the root id to pass to this API.
  2. If you have an Execution object, pass execution.getProcessInstanceId() instead of execution.getId().
  3. Verify with runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult() that the id denotes a process instance.

Example fix

// before
runtimeService.suspendProcessInstanceById(execution.getId());
// after
runtimeService.suspendProcessInstanceById(execution.getProcessInstanceId());
Defensive patterns

Strategy: type-guard

Validate before calling

ExecutionEntity e = (ExecutionEntity) runtimeService.createExecutionQuery().executionId(id).singleResult();
if (e == null || !e.isProcessInstanceType()) { throw new IllegalStateException("id is not a process instance"); }

Type guard

boolean isProcessInstance(Execution execution) {
    return execution instanceof ProcessInstance;
}

Try / catch

try {
    runtimeService.suspendProcessInstanceById(id);
} catch (ActivitiException e) {
    if (e.getMessage() != null && e.getMessage().contains("not a process instance")) {
        String rootId = runtimeService.createExecutionQuery().executionId(id).singleResult().getProcessInstanceId();
        runtimeService.suspendProcessInstanceById(rootId);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling suspendProcessInstanceById/activateProcessInstanceById with the id of a child execution or concurrent execution (ExecutionEntity from an execution query / execution listener context) rather than the process instance id.

Common situations: Storing Execution.getId() from an intermediate activity instead of the process instance id; iterating executions from createExecutionQuery() and passing every id to the process-instance-level API.

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