flowable/flowable-engine · warning · FlowableConflictException

Process instance with id

Error message

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

What it means

Flowable REST throws FlowableConflictException when suspending a process instance that is already suspended. The suspend action on PUT of the process instance rejects the redundant state change with a 409-class conflict.

Solutions

  1. Verify suspended==false via GET before sending action=suspend.
  2. Handle HTTP 409 as an idempotent success in scripts/jobs.
  3. Track suspension state in the orchestrator to avoid duplicate calls.

Example fix

// before
put(pid, {"action":"suspend"}); // 409 on second run
// after
const inst = get(pid); if (!inst.suspended) put(pid, {"action":"suspend"});
Defensive patterns

Strategy: try-catch

Validate before calling

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

Try / catch

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

Prevention

When it happens

Trigger: PUT /runtime/process-instances/{id} with {"action":"suspend"} while processInstance.isSuspended() is true; two clients suspending concurrently.

Common situations: Maintenance-window job re-runs and re-suspends; retry after timeout duplicates the suspend call; blanket suspension script run twice.

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

Appendix: source

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

        }
    }

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

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

View on GitHub (pinned to d6d39ce1c6)