{"id":"ce4f2430f54dff9e","repo":"apache/kafka","slug":"error-unregistering-mbean","errorCode":null,"errorMessage":"Error unregistering mbean","messagePattern":"Error unregistering mbean","errorType":"exception","errorClass":"KafkaException","httpStatus":null,"severity":"warning","filePath":"clients/src/main/java/org/apache/kafka/common/metrics/JmxReporter.java","lineNumber":207,"sourceCode":"            mBeanName.append(Sanitizer.jmxSanitize(entry.getValue()));\n        }\n        return mBeanName.toString();\n    }\n\n    public void close() {\n        synchronized (LOCK) {\n            for (KafkaMbean mbean : this.mbeans.values())\n                unregister(mbean);\n        }\n    }\n\n    private void unregister(KafkaMbean mbean) {\n        MBeanServer server = ManagementFactory.getPlatformMBeanServer();\n        try {\n            if (server.isRegistered(mbean.name()))\n                server.unregisterMBean(mbean.name());\n        } catch (JMException e) {\n            throw new KafkaException(\"Error unregistering mbean\", e);\n        }\n    }\n\n    private void reregister(KafkaMbean mbean) {\n        unregister(mbean);\n        try {\n            ManagementFactory.getPlatformMBeanServer().registerMBean(mbean, mbean.name());\n        } catch (JMException e) {\n            throw new KafkaException(\"Error registering mbean \" + mbean.name(), e);\n        }\n    }\n\n    private static class KafkaMbean implements DynamicMBean {\n        private final ObjectName objectName;\n        private final Map<String, KafkaMetric> metrics;\n\n        KafkaMbean(String mbeanName) throws MalformedObjectNameException {\n            this.metrics = new HashMap<>();","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/metrics/JmxReporter.java#L189-L225","documentation":"Thrown by JmxReporter.unregister(KafkaMbean) when MBeanServer.unregisterMBean() raises a JMException (most often InstanceNotFoundException) for an mbean the reporter believed was registered. Kafka treats this as an error during teardown so that silent metric leaks (mbeans never cleaned up) are visible rather than hidden. The cause carries the original JMException.","triggerScenarios":"JmxReporter.close() iterates all registered KafkaMbeans and calls unregister() on each; reregister() also calls unregister() before re-registering an updated mbean. If something else has already unregistered the mbean out from under Kafka, or the platform MBeanServer is in an inconsistent state, server.unregisterMBean throws and is wrapped here.","commonSituations":"An external JMX client or agent unregistered mbeans independently; the JVM is shutting down and platform MBeanServer is tearing down concurrently; another KafkaMetrics instance in the same JVM reused the same JMX domain and ObjectName; running under a servlet container (Tomcat/JBoss) where the container reaps mbeans on undeploy before Kafka's close runs; repeated create/close cycles of producer/consumer/AdminClient with overlapping JMX domains.","solutions":["Read the attached JMException cause — InstanceNotFoundException indicates the mbean was already gone, which is usually benign during teardown.","If you see this repeatedly in normal operation, ensure each Kafka client instance uses a distinct JMX domain prefix (KafkaJmxReporter / metrics namespace) so two instances never collide on the same ObjectName.","In embedded/container deployments, stop the Kafka client before the container reaps mbeans (lifecycle ordering) so Kafka's own close() runs first.","Suppress non-fatal teardown noise by catching KafkaException around client.close() in your shutdown hook and logging the cause rather than aborting.","Upgrade to a current Kafka version; some unregister races under repeated client create/close have been addressed in recent releases."],"exampleFix":"// before — teardown aborts on an already-gone mbean\nRuntime.getRuntime().addShutdownHook(new Thread(() -> {\n    producer.close();   // JmxReporter.unregister throws InstanceNotFoundException\n}));\n\n// after — shutdown is resilient to teardown noise\nRuntime.getRuntime().addShutdownHook(new Thread(() -> {\n    try { producer.close(Duration.ofSeconds(10)); }\n    catch (org.apache.kafka.common.KafkaException e) {\n        log.warn(\"Non-fatal error closing Kafka client / JMX reporter\", e);\n    }\n}));","handlingStrategy":"try-catch","validationCode":"// There is no pre-check; the safe pattern is to verify registration state before unregister.\nMBeanServer server = ManagementFactory.getPlatformMBeanServer();\nObjectName on = ...;\nif (server.isRegistered(on)) {\n    server.unregisterMBean(on);\n}","typeGuard":"// Narrow 'an mbean that is currently registered' before unregistering.\nstatic boolean isRegistered(ObjectName name) {\n    return ManagementFactory.getPlatformMBeanServer().isRegistered(name);\n}","tryCatchPattern":"try {\n    metrics.close(); // or metricsReporterClose()\n} catch (KafkaException e) {\n    if (e.getCause() instanceof JMException) {\n        // unregister failures are typically benign during shutdown; log and continue.\n        log.warn(\"JMX mbean unregister failed; continuing shutdown\", e.getCause());\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always close Metrics via try-with-resources; treat unregister failures as warnings, not fatal errors.","Do not manually unregister Kafka mbeans from an external MBeanServer — let JmxReporter.close() own the lifecycle.","If you run multiple Metrics instances in one JVM, give each a unique prefix/namespace to avoid cross-unregister races.","Audit for code that calls platformMBeanServer.unregisterMBean on Kafka-created names; remove it."],"tags":["kafka-clients","jmx","metrics","jmx-reporter","shutdown"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}