{"record":{"id":"8542e3cd0f925740","repo":"flowable/flowable-engine","slug":"execution-has-not-subscribed-to-a-signal-event","errorCode":null,"errorMessage":"${execution} has not subscribed to a signal event with name '${eventName}'.","messagePattern":"(.+?) has not subscribed to a signal event with name '(.+?)'\\.","errorType":"exception","errorClass":"FlowableException","httpStatus":null,"severity":"error","filePath":"modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/SignalEventReceivedCmd.java","lineNumber":99,"sourceCode":"\r\n            if (execution == null) {\r\n                throw new FlowableObjectNotFoundException(\"Cannot find execution with id '\" + executionId + \"'\", Execution.class);\r\n            }\r\n\r\n            if (execution.isSuspended()) {\r\n                throw new FlowableException(\"Cannot throw signal event '\" + eventName + \"' because \" + execution + \" is suspended\");\r\n            }\r\n\r\n            if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, execution.getProcessDefinitionId())) {\r\n                Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();\r\n                compatibilityHandler.signalEventReceived(eventName, executionId, payload, async, tenantId);\r\n                return null;\r\n            }\r\n\r\n            signalEvents = eventSubscriptionService.findSignalEventSubscriptionsByNameAndExecution(eventName, executionId);\r\n\r\n            if (signalEvents.isEmpty()) {\r\n                throw new FlowableException(execution + \" has not subscribed to a signal event with name '\" + eventName + \"'.\");\r\n            }\r\n        }\r\n\r\n        for (SignalEventSubscriptionEntity signalEventSubscriptionEntity : signalEvents) {\r\n            // We only throw the event to globally scoped signals.\r\n            // Process instance scoped signals must be thrown within the process itself\r\n            if (signalEventSubscriptionEntity.isGlobalScoped()) {\r\n\r\n                if (executionId == null && Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, signalEventSubscriptionEntity.getProcessDefinitionId())) {\r\n                    Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();\r\n                    compatibilityHandler.signalEventReceived(signalEventSubscriptionEntity, payload, async);\r\n\r\n                } else {\r\n                    processEngineConfiguration.getEventDispatcher().dispatchEvent(\r\n                            FlowableEventBuilder.createSignalEvent(FlowableEngineEventType.ACTIVITY_SIGNALED, signalEventSubscriptionEntity.getActivityId(), eventName,\r\n                                    payload, signalEventSubscriptionEntity.getExecutionId(), signalEventSubscriptionEntity.getProcessInstanceId(),\r\n                                    signalEventSubscriptionEntity.getProcessDefinitionId()), processEngineConfiguration.getEngineCfgKey());\r\n\r","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/SignalEventReceivedCmd.java#L81-L117","documentation":"The given execution exists and is active, but no signal event subscription with the supplied name is registered on it. Signals delivered to a specific execution must match a signal boundary/intermediate-catch event waiting in that execution; otherwise Flowable throws instead of silently dropping the event.","triggerScenarios":"Calling runtimeService.signalEventReceived(eventName, executionId) where the execution is not currently waiting at a signal catch event with that name (wrong name, event already fired, or execution is a parent not at the wait state).","commonSituations":"Typo or case mismatch in the signal name between BPMN and caller; duplicate signal delivery after the first one already advanced the token; signaling the process instance execution instead of the execution waiting at the boundary event.","solutions":["Confirm the BPMN node is a signal catch/boundary event and its signal name exactly matches the eventName passed (case-sensitive).","Find valid targets with runtimeService.createExecutionQuery().signalEventSubscriptionName(eventName).list() and use one of those execution ids.","Handle double-delivery: query the subscription first and skip if absent (idempotent signaling)."],"exampleFix":"// before\nruntimeService.signalEventReceived(\"OrderPaid\", executionId);\n// after\nif (!runtimeService.createExecutionQuery().executionId(executionId)\n        .signalEventSubscriptionName(\"OrderPaid\").list().isEmpty()) {\n    runtimeService.signalEventReceived(\"OrderPaid\", executionId);\n}","handlingStrategy":"validation","validationCode":"boolean waiting = !runtimeService.createExecutionQuery()\n    .executionId(executionId)\n    .signalEventSubscriptionName(signalName).list().isEmpty();\nif (waiting) runtimeService.signalEventReceived(signalName, executionId);","typeGuard":null,"tryCatchPattern":"try {\n    runtimeService.signalEventReceived(signalName, executionId);\n} catch (FlowableException e) {\n    if (e.getMessage().contains(\"has not subscribed\")) {\n        log.info(\"No active subscription for signal {} on {}\", signalName, executionId);\n    } else throw e;\n}","preventionTips":["Match signal names exactly (case-sensitive) between BPMN and caller; share them as constants","Discover target executions via ExecutionQuery.signalEventSubscriptionName(...) instead of assuming the parent execution","Guard against duplicate delivery — the first signal removes the subscription"],"tags":["flowable","signal-event","event-subscription","no-subscription"],"backgroundTag":"resource-not-found","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}