flowable/flowable-engine · warning · FlowableConflictException

Process definition with id '" + processDefinition.getId() +…

Error message

Process definition with id '" + processDefinition.getId() + " ' is already suspended

What it means

Flowable REST throws FlowableConflictException (HTTP 409) when you attempt to suspend a process definition that is already in suspended state. The REST layer checks repositoryService.isProcessDefinitionSuspended() before applying the action, so the state change would be a no-op conflict. This prevents clients from blindly toggling suspension and losing track of actual definition state.

Solutions

  1. Check suspension state first via GET /repository/process-definitions/{id} and only send the suspend action if suspended is false.
  2. Catch FlowableConflictException / handle HTTP 409 on the client and treat it as success when the desired end state (suspended) is already reached.
  3. Use the matching 'activate' action instead if the intent was to resume the definition.
  4. Serialize admin operations (locking or auditing) so two actors do not issue conflicting lifecycle actions for the same definition.

Example fix

// before
POST /flowable-rest/repository/process-definitions/myDef  {"action":"suspend"}  // 409 if already suspended

// after
// GET /flowable-rest/repository/process-definitions/myDef
if (!response.body.suspended) {
  POST /flowable-rest/repository/process-definitions/myDef  {"action":"suspend"}
}
Defensive patterns

Strategy: validation

Validate before calling

const def = await get(`/repository/process-definitions/${id}`);
if (def.suspended) return; // already suspended, skip action

Type guard

const canSuspend = (d) => typeof d?.suspended === 'boolean' && !d.suspended;

Try / catch

try { await suspendDefinition(id); } catch (e) { if (e.status === 409 || e instanceof FlowableConflictException) { /* treat as success */ } else throw e; }

Prevention

When it happens

Trigger: POST /repository/process-definitions/{processDefinitionId} with body {"action":"suspend"} (optionally with suspendProcessInstances/date) when the definition was already suspended by a previous call or by another user/job.

Common situations: Double-submitting a suspend action from an admin UI; a prior suspend with suspendProcessInstances=true already suspended the definition; retry logic re-sending a request that actually succeeded; concurrent admins acting on the same definition.

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

Appendix: source

Thrown at modules/flowable-rest/src/main/java/org/flowable/rest/service/api/repository/ProcessDefinitionResource.java:242

    }

    protected ProcessDefinitionResponse activateProcessDefinition(ProcessDefinition processDefinition, boolean suspendInstances, Date date) {
        if (!repositoryService.isProcessDefinitionSuspended(processDefinition.getId())) {
            throw new FlowableConflictException("Process definition with id '" + processDefinition.getId() + " ' is already active");
        }
        repositoryService.activateProcessDefinitionById(processDefinition.getId(), suspendInstances, date);

        ProcessDefinitionResponse response = restResponseFactory.createProcessDefinitionResponse(processDefinition);

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

    protected ProcessDefinitionResponse suspendProcessDefinition(ProcessDefinition processDefinition, boolean suspendInstances, Date date) {
        if (repositoryService.isProcessDefinitionSuspended(processDefinition.getId())) {
            throw new FlowableConflictException("Process definition with id '" + processDefinition.getId() + " ' is already suspended");
        }
        repositoryService.suspendProcessDefinitionById(processDefinition.getId(), suspendInstances, date);

        ProcessDefinitionResponse response = restResponseFactory.createProcessDefinitionResponse(processDefinition);

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

}

View on GitHub (pinned to d6d39ce1c6)