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

  1. Find a valid waiting execution with runtimeService.createExecutionQuery().processInstanceId(pid).messageEventSubscriptionName(name).singleResult() and use its id.
  2. Verify the message name matches the BPMN element's messageRef/name exactly.
  3. Guard against double delivery with idempotency checks before triggering.
  4. 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

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


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)