{"record":{"id":"c858d44a8b3927a6","repo":"flowable/flowable-engine","slug":"endpoint-registry-endpointregistry-does-not-have-c858d4","errorCode":null,"errorMessage":"Endpoint registry {endpointRegistry} does not have listenerContainers field","messagePattern":"Endpoint registry (.+?) does not have listenerContainers field","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/rabbit/RabbitChannelDefinitionProcessor.java","lineNumber":311,"sourceCode":"            try {\n                logger.debug(\"Destroying message listener {} for channel {} in tenant {}\", listenerContainer, channelModel.getKey(), tenantId);\n                ((DisposableBean) listenerContainer).destroy();\n            } catch (Exception e) {\n                throw new RuntimeException(\"Failed to destroy listener container\", e);\n            }\n        }\n\n        Field listenerContainersField = ReflectionUtils.findField(endpointRegistry.getClass(), \"listenerContainers\");\n        if (listenerContainersField != null) {\n            listenerContainersField.setAccessible(true);\n            @SuppressWarnings(\"unchecked\")\n            Map<String, MessageListenerContainer> listenerContainers = (Map<String, MessageListenerContainer>) ReflectionUtils\n                .getField(listenerContainersField, endpointRegistry);\n            if (listenerContainers != null) {\n                listenerContainers.remove(endpointId);\n            }\n        } else {\n            throw new IllegalStateException(\"Endpoint registry \" + endpointRegistry + \" does not have listenerContainers field\");\n        }\n\n        logger.info(\"Finished unregistering channel {} in tenant {}\", channelModel.getKey(), tenantId);\n    }\n\n    /**\n     * Register a new {@link RabbitListenerEndpoint} alongside the\n     * {@link RabbitListenerContainerFactory} to use to create the underlying container.\n     * <p>The {@code factory} may be {@code null} if the default factory has to be\n     * used for that endpoint.\n     */\n    protected void registerEndpoint(RabbitListenerEndpoint endpoint, RabbitListenerContainerFactory<?> factory) {\n        Assert.notNull(endpoint, \"Endpoint must not be null\");\n        Assert.hasText(endpoint.getId(), \"Endpoint id must be set\");\n\n        Assert.state(this.endpointRegistry != null, \"No RabbitListenerEndpointRegistry set\");\n        // We need to start the container immediately only if the endpoint registry is already running,\n        // otherwise we should not start it and leave it to the registry to start all the containers when it starts.","sourceCodeStart":293,"sourceCodeEnd":329,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/rabbit/RabbitChannelDefinitionProcessor.java#L293-L329","documentation":"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.","triggerScenarios":"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.","commonSituations":"Spring AMQP / Spring Boot version upgrades changing internal registry structure; accidentally registering a custom endpoint registry implementation; classpath with mismatched spring-rabbit versions.","solutions":["Check the spring-rabbit/spring-amqp version's RabbitListenerEndpointRegistry source for the actual field name","Pin a spring-amqp version compatible with this Flowable module","Verify the injected endpointRegistry bean is the standard RabbitListenerEndpointRegistry","If the field moved, unregister endpoints via the registry's public API (getListenerContainer(id).stop(); registry.unregisterListenerContainer(id)) instead of reflection"],"exampleFix":"// before\nthrow new IllegalStateException(\"Endpoint registry \" + endpointRegistry + \" does not have listenerContainers field\");\n// after\nMessageListenerContainer container = endpointRegistry.getListenerContainer(endpointId);\nif (container != null) {\n    endpointRegistry.getListenerContainer(endpointId).stop();\n}\n","handlingStrategy":"validation","validationCode":"if (ReflectionUtils.findField(endpointRegistry.getClass(), \"listenerContainers\") == null) { use endpointRegistry.getListenerContainer(endpointId) public API instead }","typeGuard":"boolean usesReflectiveRegistry = ReflectionUtils.findField(endpointRegistry.getClass(), \"listenerContainers\") != null;","tryCatchPattern":"try { unregisterChannelModel(...); } catch (IllegalStateException e) { log.warn(\"Falling back to public registry API\"); endpointRegistry.getListenerContainer(endpointId).stop(); }","preventionTips":["Pin spring-amqp versions tested with your Flowable version","Prefer public registry APIs over reflection","Smoke-test channel unregistration after every Spring Boot upgrade"],"tags":["rabbitmq","spring","reflection","version-compatibility"],"backgroundTag":"deprecated-api-usage","analyzedSha":"d6d39ce1c69ff244f2d9dc6af756a9b95e865586","analyzedAt":"2026-09-11T06:41:19.413Z","contentChangedAt":"2026-09-11T06:41:19.413Z","schemaVersion":2},"datasetVersion":"2026-09-18T11:17:12.947Z"}