flowable/flowable-engine · error · ActivitiIllegalArgumentException

Cannot throw process-instance scoped signal, since the…

Error message

Cannot throw process-instance scoped signal, since the dispatched event is not part of an ongoing process instance

What it means

Thrown as ActivitiIllegalArgumentException when a SignalThrowingEventListener is configured with processInstanceScope=true but the dispatched FlowableEngineEvent has a null processInstanceId. A process-instance-scoped signal must be thrown within an ongoing process instance, so the engine rejects the event dispatch.

Solutions

  1. Register the process-instance-scoped signal listener only for execution-related events (events that carry a processInstanceId).
  2. Set processInstanceScope="false" (or omit it) if the signal is meant to be globally scoped — global signals do not require a process instance.
  3. Guard in wrapping code: only dispatch to the listener when engineEvent.getProcessInstanceId() != null.
  4. Move the listener declaration from global engine configuration into the specific process definition where the event occurs.

Example fix

// before
<activiti:eventListener events="ENGINE_CLOSED" eventType="signal-throwing" signalName="mySignal" processInstanceScope="true"/>

// after: use global scope for engine-level events, or bind to an in-execution event
<activiti:eventListener events="ACTIVITY_SIGNALLED" eventType="signal-throwing" signalName="mySignal" processInstanceScope="true"/>
Defensive patterns

Strategy: validation

Validate before calling

if (processInstanceScope && event instanceof FlowableEngineEvent
        && ((FlowableEngineEvent) event).getProcessInstanceId() == null) {
    throw new IllegalStateException("Process-instance-scoped signal requires an in-execution event");
}

Type guard

boolean canThrowInstanceScopedSignal(FlowableEvent event, boolean processInstanceScope) {
    return !processInstanceScope
        || (event instanceof FlowableEngineEvent
            && ((FlowableEngineEvent) event).getProcessInstanceId() != null);
}

Try / catch

try {
    dispatchEvent(event);
} catch (ActivitiIllegalArgumentException e) {
    if (e.getMessage().contains("process-instance scoped signal")) {
        logger.warn("Signal dispatch skipped: no process instance on event");
    } else { throw e; }
}

Prevention

When it happens

Trigger: onEvent is invoked with isValidEvent(event) true and event instanceof FlowableEngineEvent, processInstanceScope is true, and engineEvent.getProcessInstanceId() == null — e.g. the listener is subscribed to an engine-level or deployment event.

Common situations: Configuring a signal-throwing event listener with processInstanceScope="true" on events that fire outside any process instance; listeners registered globally in ProcessEngineConfiguration eventListeners rather than in a process definition; copy-paste of listener config from an instance-scoped use case.

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

Appendix: source

Thrown at modules/flowable5-engine/src/main/java/org/activiti/engine/impl/bpmn/helper/SignalThrowingEventListener.java:43

/**
 * An {@link FlowableEventListener} that throws a signal event when an event is dispatched to it.
 * 
 * @author Frederik Heremans
 * 
 */
public class SignalThrowingEventListener extends BaseDelegateEventListener {

    protected String signalName;
    protected boolean processInstanceScope = true;

    @Override
    public void onEvent(FlowableEvent event) {
        if (isValidEvent(event) && event instanceof FlowableEngineEvent) {
            FlowableEngineEvent engineEvent = (FlowableEngineEvent) event;

            if (engineEvent.getProcessInstanceId() == null && processInstanceScope) {
                throw new ActivitiIllegalArgumentException(
                        "Cannot throw process-instance scoped signal, since the dispatched event is not part of an ongoing process instance");
            }

            CommandContext commandContext = Context.getCommandContext();
            List<SignalEventSubscriptionEntity> subscriptionEntities = null;
            if (processInstanceScope) {
                subscriptionEntities = commandContext.getEventSubscriptionEntityManager()
                        .findSignalEventSubscriptionsByProcessInstanceAndEventName(engineEvent.getProcessInstanceId(), signalName);
            } else {
                String tenantId = null;
                if (engineEvent.getProcessDefinitionId() != null) {
                    ProcessDefinition processDefinition = commandContext.getProcessEngineConfiguration()
                            .getDeploymentManager().findDeployedProcessDefinitionById(engineEvent.getProcessDefinitionId());
                    tenantId = processDefinition.getTenantId();
                }
                subscriptionEntities = commandContext.getEventSubscriptionEntityManager()
                        .findSignalEventSubscriptionsByEventName(signalName, tenantId);
            }

View on GitHub (pinned to d6d39ce1c6)