{"record":{"id":"4680e1c84f17f229","repo":"flowable/flowable-engine","slug":"failed-to-destroy-listener-container-4680e1","errorCode":null,"errorMessage":"Failed to destroy listener container","messagePattern":"Failed to destroy listener container","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/rabbit/RabbitChannelDefinitionProcessor.java","lineNumber":297,"sourceCode":"    @Override\n    public void unregisterChannelModel(ChannelModel channelModel, String tenantId, EventRepositoryService eventRepositoryService) {\n        logger.info(\"Starting to unregister channel {} in tenant {}\", channelModel.getKey(), tenantId);\n        String endpointId = getEndpointId(channelModel, tenantId);\n        // currently it is not possible to unregister a listener container\n        // In order not to do a lot of the logic that Spring does we are manually accessing the containers to remove them\n        // see https://github.com/spring-projects/spring-framework/issues/24228\n        MessageListenerContainer listenerContainer = endpointRegistry.getListenerContainer(endpointId);\n        if (listenerContainer != null) {\n            logger.debug(\"Stopping message listener {} for channel {} in tenant {}\", listenerContainer, channelModel.getKey(), tenantId);\n            listenerContainer.stop();\n        }\n\n        if (listenerContainer instanceof DisposableBean) {\n            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    }","sourceCodeStart":279,"sourceCodeEnd":315,"githubUrl":"https://github.com/flowable/flowable-engine/blob/d6d39ce1c69ff244f2d9dc6af756a9b95e865586/modules/flowable-event-registry-spring/src/main/java/org/flowable/eventregistry/spring/rabbit/RabbitChannelDefinitionProcessor.java#L279-L315","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\n((DisposableBean) listenerContainer).destroy();\n// after\ntry {\n    if (listenerContainer instanceof DisposableBean) {\n        ((DisposableBean) listenerContainer).destroy();\n    }\n} catch (Exception e) {\n    logger.warn(\"Listener container already failed to destroy cleanly\", e); // proceed with registry cleanup\n}","handlingStrategy":"try-catch","validationCode":"if (listenerContainer == null || !(listenerContainer instanceof DisposableBean)) { skip destroy }","typeGuard":"boolean safelyDestroyable = obj instanceof DisposableBean d && ((org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer) obj).isRunning();","tryCatchPattern":"try { unregisterChannelModel(channelModel, tenantId); } catch (RuntimeException e) { log.error(\"Unregister failed\", e.getCause()); /* retry or continue shutdown */ }","preventionTips":["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"],"tags":["rabbitmq","spring","lifecycle","listener-container"],"backgroundTag":"invalid-state-transition","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"}