flowable/flowable-engine · warning · FlowableException

Cannot set suspension state '' for : already in state ''.

Error message

Cannot set suspension state '' for : already in state ''.

What it means

SuspensionStateUtil.setSuspensionState(ProcessDefinitionEntity, SuspensionState) throws when the process definition's current suspension state code already equals the requested state. Suspending an already-suspended definition (or activating an already-active one) is treated as an invalid state transition, so Flowable refuses rather than performing a no-op.

Solutions

  1. Check current state first via RepositoryService.createProcessDefinitionQuery().processDefinitionKey(key).suspensionState(...) and only call suspend/activate when it differs.
  2. Wrap the suspend/activate call in try-catch for FlowableException and treat the 'already in state' case as success/idempotent.
  3. Serialize state changes (e.g. single scheduler owner or locking) to avoid duplicate suspend commands.

Example fix

// before
repositoryService.suspendProcessDefinitionByKey("orderProcess");
// after
boolean active = repositoryService.createProcessDefinitionQuery().processDefinitionKey("orderProcess").active().count() > 0;
if (active) {
    repositoryService.suspendProcessDefinitionByKey("orderProcess");
}
Defensive patterns

Strategy: validation

Validate before calling

boolean active = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).active().count() > 0;
boolean suspended = repositoryService.createProcessDefinitionQuery().processDefinitionKey(key).suspended().count() > 0;
// suspend only if active, activate only if suspended

Try / catch

try { repositoryService.suspendProcessDefinitionByKey(key); } catch (FlowableException e) { if (e.getMessage().contains("already in state")) { /* idempotent no-op */ } else throw e; }

Prevention

When it happens

Trigger: Calling RuntimeService.suspendProcessDefinitionByKey/ById (or activate*) on a definition that is already in the target state; the check compares getSuspensionState() with state.getStateCode().

Common situations: Admin UI double-clicking a suspend button; scheduled job suspending definitions without checking current state; retry logic re-issuing a suspend command after a partial failure; cluster nodes racing to suspend 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/547035dda64ff6e9. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/persistence/entity/SuspensionStateUtil.java:41

import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.task.api.history.HistoricTaskLogEntryType;
import org.flowable.task.service.TaskServiceConfiguration;
import org.flowable.task.service.impl.BaseHistoricTaskLogEntryBuilderImpl;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;

import tools.jackson.databind.node.ObjectNode;

/**
 * Helper class for suspension state
 * 
 * @author Tijs Rademakers
 */
public class SuspensionStateUtil {

    public static void setSuspensionState(ProcessDefinitionEntity processDefinitionEntity, SuspensionState state) {
        if (processDefinitionEntity.getSuspensionState() == state.getStateCode()) {
            throw new FlowableException("Cannot set suspension state '" + state + "' for " + processDefinitionEntity + "': already in state '" + state + "'.");
        }
        processDefinitionEntity.setSuspensionState(state.getStateCode());
        dispatchStateChangeEvent(processDefinitionEntity, state);
    }

    public static void setSuspensionState(ExecutionEntity executionEntity, SuspensionState state) {
        if (executionEntity.getSuspensionState() == state.getStateCode()) {
            throw new FlowableException("Cannot set suspension state '" + state + "' for " + executionEntity + "': already in state '" + state + "'.");
        }
        executionEntity.setSuspensionState(state.getStateCode());
        dispatchStateChangeEvent(executionEntity, state);
    }

    public static void setSuspensionState(TaskEntity taskEntity, SuspensionState state) {
        if (taskEntity.getSuspensionState() == state.getStateCode()) {
            throw new FlowableException("Cannot set suspension state '" + state + "' for " + taskEntity + "': already in state '" + state + "'.");
        }
        taskEntity.setSuspensionState(state.getStateCode());

View on GitHub (pinned to d6d39ce1c6)