flowable/flowable-engine · error · IllegalStateException

Endpoint registry does not have listenerContainers field

Error message

Endpoint registry {endpointRegistry} does not have listenerContainers field

What it means

Thrown when unregisterChannelModel tries to reflectively access a 'listenerContainers' field on the Spring RabbitListenerEndpointRegistry and the field does not exist. This indicates the endpoint registry class is not the expected RabbitMQ/Spring AMQP implementation (Spring AMQP changed internals) or a wrong bean was injected.

Solutions

  1. Check the spring-rabbit/spring-amqp version's RabbitListenerEndpointRegistry source for the actual field name
  2. Pin a spring-amqp version compatible with this Flowable module
  3. Verify the injected endpointRegistry bean is the standard RabbitListenerEndpointRegistry
  4. If the field moved, unregister endpoints via the registry's public API (getListenerContainer(id).stop(); registry.unregisterListenerContainer(id)) instead of reflection

Example fix

// before
throw new IllegalStateException("Endpoint registry " + endpointRegistry + " does not have listenerContainers field");
// after
MessageListenerContainer container = endpointRegistry.getListenerContainer(endpointId);
if (container != null) {
    endpointRegistry.getListenerContainer(endpointId).stop();
}
Defensive patterns

Strategy: validation

Validate before calling

if (ReflectionUtils.findField(endpointRegistry.getClass(), "listenerContainers") == null) { use endpointRegistry.getListenerContainer(endpointId) public API instead }

Type guard

boolean usesReflectiveRegistry = ReflectionUtils.findField(endpointRegistry.getClass(), "listenerContainers") != null;

Try / catch

try { unregisterChannelModel(...); } catch (IllegalStateException e) { log.warn("Falling back to public registry API"); endpointRegistry.getListenerContainer(endpointId).stop(); }

Prevention

When it happens

Trigger: Calling unregisterChannelModel with an endpointRegistry bean whose class lacks a private Map 'listenerContainers' field — typically after upgrading spring-amqp to a version that renamed/changed this internal field.

Common situations: Spring AMQP / Spring Boot version upgrades changing internal registry structure; accidentally registering a custom endpoint registry implementation; classpath with mismatched spring-rabbit versions.

Understand the failure class

Background: "is deprecated and will be removed" — deprecation warnings for old API names, keywords, and options, and how to migrate before the removal release — this error's family across 29 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/c858d44a8b3927a6. Report an issue: GitHub.

Appendix: source

Thrown at modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/rabbit/RabbitChannelDefinitionProcessor.java:311

            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);
    }

    /**
     * Register a new {@link RabbitListenerEndpoint} alongside the
     * {@link RabbitListenerContainerFactory} to use to create the underlying container.
     * <p>The {@code factory} may be {@code null} if the default factory has to be
     * used for that endpoint.
     */
    protected void registerEndpoint(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory<?> factory) {
        Assert.notNull(endpoint, "Endpoint must not be null");
        Assert.hasText(endpoint.getId(), "Endpoint id must be set");

        Assert.state(this.endpointRegistry != null, "No RabbitListenerEndpointRegistry set");
        // We need to start the container immediately only if the endpoint registry is already running,
        // otherwise we should not start it and leave it to the registry to start all the containers when it starts.

View on GitHub (pinned to d6d39ce1c6)