flowable/flowable-engine · error · RuntimeException
Failed to destroy listener container
Error message
Failed to destroy listener container
What it means
Thrown by unregisterChannelModel when destroying the Spring RabbitMQ MessageListenerContainer (via DisposableBean.destroy()) fails. The original exception is wrapped in a RuntimeException so the channel-unregistration flow aborts rather than leaving a half-destroyed container. It signals an underlying problem inside the container's own shutdown logic, not in Flowable itself.
Solutions
- Inspect the wrapped cause (e.getCause()) for the real failure — usually a RabbitMQ connection/amqp exception
- Verify the RabbitMQ broker is reachable and the connection factory is valid before unregistering
- Ensure the listener container was fully started/registered before attempting destroy; skip/replace containers already stopped
- Check for concurrent unregistration of the same endpointId and synchronize if needed
- Upgrade Spring AMQP/Flowable versions if the container destroy() has a known lifecycle bug
Example fix
// before
((DisposableBean) listenerContainer).destroy();
// after
try {
if (listenerContainer instanceof DisposableBean) {
((DisposableBean) listenerContainer).destroy();
}
} catch (Exception e) {
logger.warn("Listener container already failed to destroy cleanly", e); // proceed with registry cleanup
} Defensive patterns
Strategy: try-catch
Validate before calling
if (listenerContainer == null || !(listenerContainer instanceof DisposableBean)) { skip destroy } Type guard
boolean safelyDestroyable = obj instanceof DisposableBean d && ((org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer) obj).isRunning();
Try / catch
try { unregisterChannelModel(channelModel, tenantId); } catch (RuntimeException e) { log.error("Unregister failed", e.getCause()); /* retry or continue shutdown */ } Prevention
- Ensure broker connectivity before lifecycle operations
- Unregister channels during orderly context shutdown, not mid-failure
- Log and inspect the wrapped cause
- Avoid concurrent unregistration of the same endpoint
When it happens
Trigger: Calling eventRegistry/rabbit channel unregistration for a channel whose listener container's destroy() method throws (e.g. underlying RabbitMQ connection/consumer cancellation errors, or a container whose afterPropertiesSet/stop lifecycle is in an inconsistent state).
Common situations: Shutting down or redeploying channel definitions while the broker is unreachable; concurrent shutdown of the application context racing with channel unregistration; containers created incorrectly due to earlier configuration errors.
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
- ackMode in definition
- Channel definition cannot resolve as a String[] or a String…
- Could not register rabbit listener endpoint on
- Could not register rabbit listener endpoint on
- Could not resolve the JmsListenerContainerFactory to use for
AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11).
Data as JSON: /api/errors/4680e1c84f17f229.
Report an issue: GitHub.
Appendix: source
Thrown at modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/rabbit/RabbitChannelDefinitionProcessor.java:297
@Override
public void unregisterChannelModel(ChannelModel channelModel, String tenantId, EventRepositoryService eventRepositoryService) {
logger.info("Starting to unregister channel {} in tenant {}", channelModel.getKey(), tenantId);
String endpointId = getEndpointId(channelModel, tenantId);
// currently it is not possible to unregister a listener container
// In order not to do a lot of the logic that Spring does we are manually accessing the containers to remove them
// see https://github.com/spring-projects/spring-framework/issues/24228
MessageListenerContainer listenerContainer = endpointRegistry.getListenerContainer(endpointId);
if (listenerContainer != null) {
logger.debug("Stopping message listener {} for channel {} in tenant {}", listenerContainer, channelModel.getKey(), tenantId);
listenerContainer.stop();
}
if (listenerContainer instanceof DisposableBean) {
try {
logger.debug("Destroying message listener {} for channel {} in tenant {}", listenerContainer, channelModel.getKey(), tenantId);
((DisposableBean) listenerContainer).destroy();
} catch (Exception e) {
throw new RuntimeException("Failed to destroy listener container", e);
}
}
Field listenerContainersField = ReflectionUtils.findField(endpointRegistry.getClass(), "listenerContainers");
if (listenerContainersField != null) {
listenerContainersField.setAccessible(true);
@SuppressWarnings("unchecked")
Map<String, MessageListenerContainer> listenerContainers = (Map<String, MessageListenerContainer>) ReflectionUtils
.getField(listenerContainersField, endpointRegistry);
if (listenerContainers != null) {
listenerContainers.remove(endpointId);
}
} else {
throw new IllegalStateException("Endpoint registry " + endpointRegistry + " does not have listenerContainers field");
}
logger.info("Finished unregistering channel {} in tenant {}", channelModel.getKey(), tenantId);
}View on GitHub (pinned to d6d39ce1c6)