flowable/flowable-engine · error · ActivitiException

Cannot throw signal event

Error message

Cannot throw signal event '<eventName>' because execution '<executionId>' is suspended

What it means

SignalEventReceivedCmd throws ActivitiException when the execution referenced by executionId is suspended, preventing a signal event from being thrown into it. Suspended executions must not receive events until activated, so the command aborts, guarding the process state machine against modifications during suspension.

Solutions

  1. Resume with runtimeService.activateProcessInstanceById(instanceId) before signaling.
  2. Buffer the event externally (queue/DB) and deliver it once the instance is activated.
  3. Check suspension state via ProcessInstanceQuery.suspended() and defer or discard the signal.

Example fix

// before
runtimeService.signalEventReceived("order-approved", executionId);
// after
if (runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).suspended().count() == 0) {
    runtimeService.signalEventReceived("order-approved", executionId);
} else {
    // buffer event for delivery after activation
}
Defensive patterns

Strategy: validation

Validate before calling

boolean suspended = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).suspended().count() > 0;
if (!suspended) runtimeService.signalEventReceived(eventName, executionId);

Try / catch

try {
    runtimeService.signalEventReceived(eventName, executionId);
} catch (ActivitiException e) {
    logger.warn("Execution {} suspended; buffering signal {}", executionId, eventName);
    eventBuffer.add(new PendingSignal(eventName, executionId));
}

Prevention

When it happens

Trigger: Calling runtimeService.signalEventReceived(eventName, executionId) while the instance or execution was paused via suspendProcessInstanceById / suspendProcessInstanceByProcessDefinitionId.

Common situations: Maintenance suspension active when an external event (approval callback, message-queue listener) tries to deliver a signal; bulk suspension sweep racing with event delivery.

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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/cmd/SignalEventReceivedCmd.java:82

    @Override
    public Void execute(CommandContext commandContext) {

        List<SignalEventSubscriptionEntity> signalEvents = null;

        if (executionId == null) {
            signalEvents = commandContext.getEventSubscriptionEntityManager().findSignalEventSubscriptionsByEventName(eventName, tenantId);

        } else {

            ExecutionEntity execution = commandContext.getExecutionEntityManager().findExecutionById(executionId);

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

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

            signalEvents = commandContext.getEventSubscriptionEntityManager().findSignalEventSubscriptionsByNameAndExecution(eventName, executionId);

            if (signalEvents.isEmpty()) {
                throw new ActivitiException("Execution '" + executionId + "' 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
            if (signalEventSubscriptionEntity.isGlobalScoped()) {
                signalEventSubscriptionEntity.eventReceived(payload, async);
            }
        }

View on GitHub (pinned to d6d39ce1c6)