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
- Register the process-instance-scoped signal listener only for execution-related events (events that carry a processInstanceId).
- Set processInstanceScope="false" (or omit it) if the signal is meant to be globally scoped — global signals do not require a process instance.
- Guard in wrapping code: only dispatch to the listener when engineEvent.getProcessInstanceId() != null.
- 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
- Only set processInstanceScope="true" when the listener event always carries a processInstanceId.
- Use global signal scope (default) for engine-level events.
- Declare signal-throwing listeners inside the process definition, not in global engine event listeners, when instance-scoped.
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
- Cannot throw process-instance scoped message, since the…
- Cannot create 'script' task listener. Missing ScriptInfo.
- Class does not implement
- Could not handle signal: no process instance started
- Custom properties resolver delegate expression " +…
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)