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

After destroying the listener container, unregisterChannelModel removes the endpoint from the endpoint registry's internal 'listenerContainers' map via reflection. If that field does not exist on the registry class (ReflectionUtils.findField returns null), it throws an IllegalStateException. This is an internal-structure assumption failure, typically caused by a Spring/Flowable version change.

Solutions

  1. Check Spring Framework / Flowable versions for compatibility; the processor relies on the registry's 'listenerContainers' field.
  2. Upgrade or downgrade Flowable to a version matching your Spring version.
  3. If a custom registry is used, add/maintain the listenerContainers field or override the processor.
  4. Report/patch: replace reflection with a proper registry API if one exists in your version.

Example fix

// before: custom registry without the expected internal field
class MyEndpointRegistry { /* no listenerContainers field */ }

// after: use/extend the supported registry implementation
class MyEndpointRegistry extends DefaultJmsListenerEndpointRegistry { /* has listenerContainers */ }
Defensive patterns

Strategy: validation

Validate before calling

if (ReflectionUtils.findField(endpointRegistry.getClass(), "listenerContainers") == null) {
    throw new IllegalStateException("Incompatible endpoint registry implementation: " + endpointRegistry.getClass());
}

Type guard

boolean isSupportedRegistry(Object registry) {
    return registry instanceof org.springframework.jms.config.DefaultJmsListenerEndpointRegistry;
}

Try / catch

try {
    processor.unregisterChannelModel(channelModel, tenantId);
} catch (IllegalStateException e) {
    log.error("Registry implementation incompatible with processor; check Spring/Flowable versions", e);
}

Prevention

When it happens

Trigger: Unregistering a JMS channel endpoint when the endpointRegistry object's class has no 'listenerContainers' field — e.g. a different AbstractMessageListenerContainer registry implementation or an upgraded Spring Framework that renamed/restructured the field.

Common situations: Spring Framework upgrade changing internals of the endpoint registry; Flowable running against an unsupported Spring version; custom endpoint registry implementations.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/jms/JmsChannelModelProcessor.java:191

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

    /**
     * Register a new {@link JmsListenerEndpoint} alongside the
     * {@link JmsListenerContainerFactory} 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(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
        Assert.notNull(endpoint, "Endpoint must not be null");
        Assert.hasText(endpoint.getId(), "Endpoint id must be set");

        Assert.state(this.endpointRegistry != null, "No JmsListenerEndpointRegistry set");

        // We need to start the container immediately only if the endpoint registry is already running,

View on GitHub (pinned to d6d39ce1c6)