Note: I'm not entirely sure whether this is a bug or a feature request — the current behaviour may be by design, but it leads to silent data loss and hung process instances. Happy to be guided by the maintainers.
Describe the bug
When using an async HTTP Service Task followed by an Intermediate Catching Event, there is a race condition where the Kafka message can arrive before Flowable has registered the event subscription.
With flowable:async="true" flowable:parallelInSameTransaction="false", the token moves to the Catching Event in a separate transaction picked up by the job executor asynchronously. If the external service publishes its response to Kafka before the job executor runs, the message arrives with no subscription yet registered. BpmnEventRegistryEventConsumer silently discards it — eventHandled() returns false, no exception, no log. The process instance waits forever.
t=0ms Async HTTP Task fires
t=50ms External service publishes Kafka message
t=70ms Message consumed → no subscription found → silently discarded
t=800ms Job executor moves token to Catching Event → waits forever
This is hard to reproduce locally (fast job executor) but common under production load.
Expected behavior
When no subscription is found, BpmnEventRegistryEventConsumer should provide a hook for callers to act on it — so that messaging infrastructure (e.g. Spring Kafka non-blocking retry) can re-deliver the message after a delay, by which time the subscription will exist.
Code
BPMN pattern that triggers the issue:
<serviceTask id="httpTask" flowable:async="true" flowable:parallelInSameTransaction="false" flowable:type="http"/>
<intermediateCatchEvent id="waitForResponse">
<extensionElements>
<flowable:eventType>myEvent</flowable:eventType>
<flowable:eventCorrelationParameter name="correlation_id" value="${correlation_id}"/>
</extensionElements>
</intermediateCatchEvent>
<sequenceFlow sourceRef="httpTask" targetRef="waitForResponse"/>
Current workaround — subclassing BpmnEventRegistryEventConsumer:
class NoSubscriberEventConsumer(
processEngineConfiguration: ProcessEngineConfigurationImpl,
) : BpmnEventRegistryEventConsumer(processEngineConfiguration) {
override fun eventReceived(eventInstance: EventInstance): EventRegistryProcessingInfo {
val result = super.eventReceived(eventInstance)
if (!result.eventHandled()) {
throw NoEventSubscriptionFoundException("No subscription found for key '${eventInstance.eventKey}'")
}
return result
}
}
Suggested fix
Add a protected template method called when findEventSubscriptions() returns empty:
// no-op by default — backwards compatible
protected void handleNoSubscriptionsFound(EventInstance eventInstance) { }
This is broker-agnostic, fully backwards compatible, and gives applications a clean extension point to wire in their own recovery mechanism.
Additional context
- Flowable version: 7.2.0
- Database: PostgreSQL
- Spring Boot via
flowable-spring-boot-starter
- Kafka inbound channels (
.channel files with "type": "kafka")
Describe the bug
When using an async HTTP Service Task followed by an Intermediate Catching Event, there is a race condition where the Kafka message can arrive before Flowable has registered the event subscription.
With
flowable:async="true" flowable:parallelInSameTransaction="false", the token moves to the Catching Event in a separate transaction picked up by the job executor asynchronously. If the external service publishes its response to Kafka before the job executor runs, the message arrives with no subscription yet registered.BpmnEventRegistryEventConsumersilently discards it —eventHandled()returnsfalse, no exception, no log. The process instance waits forever.This is hard to reproduce locally (fast job executor) but common under production load.
Expected behavior
When no subscription is found,
BpmnEventRegistryEventConsumershould provide a hook for callers to act on it — so that messaging infrastructure (e.g. Spring Kafka non-blocking retry) can re-deliver the message after a delay, by which time the subscription will exist.Code
BPMN pattern that triggers the issue:
Current workaround — subclassing
BpmnEventRegistryEventConsumer:Suggested fix
Add a protected template method called when
findEventSubscriptions()returns empty:This is broker-agnostic, fully backwards compatible, and gives applications a clean extension point to wire in their own recovery mechanism.
Additional context
flowable-spring-boot-starter.channelfiles with"type": "kafka")