flowable/flowable-engine · error · FlowableException

Cannot throw signal event '${eventName}' because ${execution

Error message

Cannot throw signal event '${eventName}' because ${execution} is suspended

What it means

The target execution for a signal event exists but is suspended, and Flowable refuses to deliver the signal into a frozen execution. Signals are state-changing operations, so they are blocked while suspension is in effect.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/SignalEventReceivedCmd.java:87

    @Override
    public Void execute(CommandContext commandContext) {

        List<SignalEventSubscriptionEntity> signalEvents = null;

        ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
        EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
        if (executionId == null) {
            signalEvents = eventSubscriptionService.findSignalEventSubscriptionsByEventName(eventName, tenantId);
        } else {

            ExecutionEntity execution = CommandContextUtil.getExecutionEntityManager(commandContext).findById(executionId);

            if (execution == null) {
                throw new FlowableObjectNotFoundException("Cannot find execution with id '" + executionId + "'", Execution.class);
            }

            if (execution.isSuspended()) {
                throw new FlowableException("Cannot throw signal event '" + eventName + "' because " + execution + " is suspended");
            }

            if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, execution.getProcessDefinitionId())) {
                Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
                compatibilityHandler.signalEventReceived(eventName, executionId, payload, async, tenantId);
                return null;
            }

            signalEvents = eventSubscriptionService.findSignalEventSubscriptionsByNameAndExecution(eventName, executionId);

            if (signalEvents.isEmpty()) {
                throw new FlowableException(execution + " has not subscribed to a signal event with name '" + eventName + "'.");
            }
        }

        for (SignalEventSubscriptionEntity signalEventSubscriptionEntity : signalEvents) {
            // We only throw the event to globally scoped signals.
            // Process instance scoped signals must be thrown within the process itself

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Activate the instance with runtimeService.activateProcessInstanceById(execution.getProcessInstanceId()) before signaling.
  2. Buffer the signal: store the event payload and deliver it after resumption (or via a job/retry).
  3. Check execution.isSuspended() (or the suspended flag on the query result) and handle gracefully in the caller.

Example fix

// before
runtimeService.signalEventReceived(eventName, executionId);
// after
Execution e = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
if (e != null && !e.isSuspended()) {
    runtimeService.signalEventReceived(eventName, executionId);
}
Defensive patterns

Strategy: try-catch

Validate before calling

Execution e = runtimeService.createExecutionQuery().executionId(executionId).singleResult();
if (e != null && !e.isSuspended()) runtimeService.signalEventReceived(signalName, executionId);

Try / catch

try {
    runtimeService.signalEventReceived(signalName, executionId);
} catch (FlowableException e) {
    if (e.getMessage().contains("is suspended")) {
        pendingSignalStore.save(signalName, executionId); // deliver after activation
    } else throw e;
}

Prevention

When it happens

Trigger: Calling runtimeService.signalEventReceived(eventName, executionId) on an execution suspended via suspendProcessInstanceById/DefinitionId or a suspend job definition.

Common situations: External events (payments, callbacks) arriving while an administrator suspended the process for maintenance; instances suspended because their process definition was suspended.

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