{"record":{"id":"0118ac524cd1dfe7","repo":"flowable/flowable-engine","slug":"cannot-throw-process-instance-scoped-signal-since","errorCode":null,"errorMessage":"Cannot throw process-instance scoped signal, since the dispatched event is not part of an ongoing process instance","messagePattern":"Cannot throw process-instance scoped signal, since the dispatched event is not part of an ongoing process instance","errorType":"exception","errorClass":"FlowableIllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/SignalThrowingEventListener.java","lineNumber":46,"sourceCode":"import org.flowable.eventsubscription.service.impl.persistence.entity.SignalEventSubscriptionEntity;\n\n/**\n * An {@link FlowableEventListener} that throws a signal event when an event is dispatched to it.\n * \n * @author Frederik Heremans\n * \n */\npublic class SignalThrowingEventListener extends BaseDelegateEventListener {\n\n    protected String signalName;\n    protected boolean processInstanceScope = true;\n\n    @Override\n    public void onEvent(FlowableEvent event) {\n        if (isValidEvent(event) && event instanceof FlowableEngineEvent engineEvent) {\n\n            if (engineEvent.getProcessInstanceId() == null && processInstanceScope) {\n                throw new FlowableIllegalArgumentException(\"Cannot throw process-instance scoped signal, since the dispatched event is not part of an ongoing process instance\");\n            }\n\n            CommandContext commandContext = Context.getCommandContext();\n            ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);\n            EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();\n            List<SignalEventSubscriptionEntity> subscriptionEntities = null;\n            if (processInstanceScope) {\n                subscriptionEntities = eventSubscriptionService.findSignalEventSubscriptionsByProcessInstanceAndEventName(engineEvent.getProcessInstanceId(), signalName);\n            } else {\n                String tenantId = null;\n                if (engineEvent.getProcessDefinitionId() != null) {\n                    ProcessDefinition processDefinition = processEngineConfiguration.getDeploymentManager()\n                            .findDeployedProcessDefinitionById(engineEvent.getProcessDefinitionId());\n                    tenantId = processDefinition.getTenantId();\n                }\n                subscriptionEntities = eventSubscriptionService.findSignalEventSubscriptionsByEventName(signalName, tenantId);\n            }\n","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/SignalThrowingEventListener.java#L28-L64","documentation":"SignalThrowingEventListener.onEvent throws this FlowableIllegalArgumentException when a signal is configured with processInstanceScope=true but the dispatched engine event has a null processInstanceId, so a process-instance-scoped signal cannot be delivered.","triggerScenarios":"A 'signal' event listener with processInstanceScope=true receives a FlowableEngineEvent whose getProcessInstanceId() is null — engine/deployment-level events, or events fired outside a running process instance.","commonSituations":"Global listeners reacting to all FlowableEventType values including engine events; signals thrown on process-start events before the instance id is assigned (rare) or on job/entity events outside process scope; copy-pasted listener config where scope was changed to process-instance but event source wasn't.","solutions":["Register the listener only for event types that carry a processInstanceId, or add a wrapper listener that filters on event.getProcessInstanceId() != null.","If the signal should be global (all process instances), set processInstanceScope=false so the execution-scoped branch is used.","Trigger the signal via runtimeService.signalEventReceived with explicit delivery semantics if a specific subscription is targeted.","Audit listener XML config: ensure 'scope' attribute matches the events the listener is bound to."],"exampleFix":"// before\n<flowable:event-listener events=\"*\" class=\"SignalThrowingEventListener\" scope=\"processInstance\" />\n\n// after\n<flowable:event-listener events=\"ACTIVITY_SIGNAL_WAITING,JOB_EXECUTION_SUCCESS\" class=\"SignalThrowingEventListener\" scope=\"processInstance\" />","handlingStrategy":"type-guard","validationCode":"if (processInstanceScope && (event == null || !(event instanceof FlowableEngineEvent)\n        || ((FlowableEngineEvent) event).getProcessInstanceId() == null)) {\n    throw new IllegalArgumentException(\"process-scoped signal requires an engine event with processInstanceId\");\n}","typeGuard":"static boolean supportsProcessScopeSignal(FlowableEvent event) {\n    return event instanceof FlowableEngineEvent && ((FlowableEngineEvent) event).getProcessInstanceId() != null;\n}","tryCatchPattern":"try {\n    listener.onEvent(event);\n} catch (FlowableIllegalArgumentException e) {\n    if (e.getMessage().contains(\"process-instance scoped signal\")) {\n        logger.warn(\"ignored process-scoped signal for event {}\", event.getType());\n    }\n}","preventionTips":["Set scope=false for signals intended to reach all instances.","Bind process-scoped signal listeners only to events that carry a processInstanceId.","Review listener XML scope attributes whenever event bindings change."],"tags":["flowable","bpmn","event-listener","signal","missing-process-instance"],"backgroundTag":"missing-required-argument","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}