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
- Check GET /runtime/process-instances/{id} for suspended==true before sending action=activate.
- Treat HTTP 409 from this call as success in idempotent job code.
- 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
- Check suspended state before activate
- Treat 409 as idempotent success
- Avoid re-firing activation jobs
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
- Process instance with id
- A group with id '' already exists.
- Engine property already exists
- Process definition with id '" + processDefinition.getId() +…
- User '' is already part of group ''.
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)