flowable/flowable-engine · error · FlowableException
execution + " does not have a subscription to a message…
Error message
execution + " does not have a subscription to a message event with name '" + messageName + "'"
What it means
When delivering a message, MessageEventReceivedCmd queries event subscriptions of type message by name and executionId. If none exist it throws FlowableException '<execution> does not have a subscription to a message event with name '<name>''. The execution is not currently waiting at a message event with that name, so there is nothing to deliver to.
Solutions
- Find a valid waiting execution with runtimeService.createExecutionQuery().processInstanceId(pid).messageEventSubscriptionName(name).singleResult() and use its id.
- Verify the message name matches the BPMN element's messageRef/name exactly.
- Guard against double delivery with idempotency checks before triggering.
- Catch FlowableException (or FlowableException subtype) around messageEventReceived and treat 'no subscription' as 'message too late'.
Example fix
// before
runtimeService.messageEventReceived("orderReceived", processInstanceId);
// after
Execution exec = runtimeService.createExecutionQuery()
.processInstanceId(pid)
.messageEventSubscriptionName("orderReceived")
.singleResult();
if (exec != null) {
runtimeService.messageEventReceived("orderReceived", exec.getId());
} Defensive patterns
Strategy: validation
Validate before calling
Execution exec = runtimeService.createExecutionQuery()
.processInstanceId(pid)
.messageEventSubscriptionName(messageName)
.singleResult();
if (exec == null) {
log.info("No waiting subscription for message {} — skipping delivery", messageName);
return;
} Type guard
boolean hasMessageSubscription(String pid, String name) {
return runtimeService.createExecutionQuery()
.processInstanceId(pid)
.messageEventSubscriptionName(name)
.count() > 0;
} Try / catch
try {
runtimeService.messageEventReceived(messageName, executionId, payload);
} catch (FlowableException e) {
if (e.getMessage().contains("does not have a subscription")) {
log.warn("Message {} arrived too late for execution {}", messageName, executionId);
return;
}
throw e;
} Prevention
- Resolve the waiting execution id via ExecutionQuery, never assume the process instance id
- Make message delivery idempotent to survive duplicates
- Handle boundary-event timeouts which remove subscriptions asynchronously
When it happens
Trigger: Calling runtimeService.messageEventReceived(messageName, executionId) where the execution has already passed the message event, the name doesn't match the subscription, or the executionId points to a scope with no message subscription.
Common situations: Race conditions where the message arrives twice and the first delivery moved the process past the boundary event; mistyped message names; triggering on the wrong execution id (e.g. process instance id instead of the waiting execution id); timeouts already cancelled the subscription.
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
- At least one correlation parameter value must be provided…
- At least one correlation parameter value must be provided…
- Cannot start process instance by message: no subscription…
- Cannot throw process-instance scoped message, since the…
- configuration is null
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/1910290da83f8246.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/cmd/MessageEventReceivedCmd.java:82
@Override
protected Void execute(CommandContext commandContext, ExecutionEntity execution) {
if (messageName == null) {
throw new FlowableIllegalArgumentException("messageName cannot be null");
}
if (Flowable5Util.isFlowable5ProcessDefinitionId(commandContext, execution.getProcessDefinitionId())) {
Flowable5CompatibilityHandler compatibilityHandler = Flowable5Util.getFlowable5CompatibilityHandler();
compatibilityHandler.messageEventReceived(messageName, executionId, payload, async);
return null;
}
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
List<EventSubscriptionEntity> eventSubscriptions = eventSubscriptionService.findEventSubscriptionsByNameAndExecution(MessageEventHandler.EVENT_HANDLER_TYPE, messageName, executionId);
if (eventSubscriptions.isEmpty()) {
throw new FlowableException(execution + " does not have a subscription to a message event with name '" + messageName + "'");
}
// there can be only one:
EventSubscriptionEntity eventSubscriptionEntity = eventSubscriptions.get(0);
EventSubscriptionUtil.eventReceived(eventSubscriptionEntity, payload, async);
return null;
}
@Override
protected String getSuspendedExceptionMessagePrefix() {
return "Cannot receive message for";
}
}
View on GitHub (pinned to d6d39ce1c6)