flowable/flowable-engine · warning · FlowableConflictException

Process instance with id

Error message

Process instance with id '${processInstanceId}' is already active.

What it means

Flowable REST throws FlowableConflictException when activating a process instance that is not suspended. The activate action (action=activate on PUT of the process instance) only makes sense on a suspended instance; otherwise the operation would be a no-op conflict.

Solutions

  1. Check GET /runtime/process-instances/{id} for suspended==true before sending action=activate.
  2. Treat HTTP 409 from this call as success in idempotent job code.
  3. Scope suspension/activation queries to only the affected instances.

Example fix

// before
put(pid, {"action":"activate"}); // 409 if already active
// after
const inst = get(pid); if (inst.suspended) put(pid, {"action":"activate"});
Defensive patterns

Strategy: try-catch

Validate before calling

const inst = await get(`/runtime/process-instances/${pid}`); if (inst.suspended) await put(pid, {action:'activate'});

Try / catch

try { put(pid, {action:'activate'}); } catch (e) { if (e.status === 409) return; throw e; }

Prevention

When it happens

Trigger: PUT /runtime/process-instances/{id} with {"action":"activate"} while the instance is running (suspended == false); racing activation from two clients.

Common situations: Scheduled resume job re-fires after the instance was already activated; retry logic re-sends an activate action without checking state; activation of instances selected by an overly broad query.

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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceResource.java:322

            }
            DynamicEmbeddedSubProcessBuilder subProcessBuilder = new DynamicEmbeddedSubProcessBuilder();
            subProcessBuilder.id(injectActivityRequest.getId())
                .processDefinitionId(injectActivityRequest.getProcessDefinitionId());
            
            if (injectActivityRequest.getTaskId() != null) {
                dynamicBpmnService.injectParallelEmbeddedSubProcess(injectActivityRequest.getTaskId(), subProcessBuilder);
            } else {
                dynamicBpmnService.injectEmbeddedSubProcessInProcessInstance(processInstanceId, subProcessBuilder);
            }
        
        } else {
            throw new FlowableIllegalArgumentException("injection type is not supported " + injectActivityRequest.getInjectionType());
        }
    }

    protected ProcessInstanceResponse activateProcessInstance(ProcessInstance processInstance) {
        if (!processInstance.isSuspended()) {
            throw new FlowableConflictException("Process instance with id '" + processInstance.getId() + "' is already active.");
        }
        runtimeService.activateProcessInstanceById(processInstance.getId());

        ProcessInstanceResponse response = restResponseFactory.createProcessInstanceResponse(processInstance);

        // No need to re-fetch the instance, just alter the suspended state of the result-object
        response.setSuspended(false);
        return response;
    }

    protected ProcessInstanceResponse suspendProcessInstance(ProcessInstance processInstance) {
        if (processInstance.isSuspended()) {
            throw new FlowableConflictException("Process instance with id '" + processInstance.getId() + "' is already suspended.");
        }
        runtimeService.suspendProcessInstanceById(processInstance.getId());

        ProcessInstanceResponse response = restResponseFactory.createProcessInstanceResponse(processInstance);

View on GitHub (pinned to d6d39ce1c6)