{"id":"386aa9f0a03dbd90","repo":"apache/kafka","slug":"failed-closing-plugin","errorCode":null,"errorMessage":"failed closing plugin","messagePattern":"failed closing plugin","errorType":"exception","errorClass":"KafkaException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/internals/Plugin.java","lineNumber":130,"sourceCode":"            plugins.add(wrapInstance(instance, metrics, key));\n        }\n        return plugins;\n    }\n\n    @Override\n    public T get() {\n        return instance;\n    }\n\n    @Override\n    public void close() throws Exception {\n        AtomicReference<Throwable> firstException = new AtomicReference<>();\n        if (instance instanceof AutoCloseable) {\n            Utils.closeQuietly((AutoCloseable) instance, instance.getClass().getSimpleName(), firstException);\n        }\n        pluginMetrics.ifPresent(metrics -> Utils.closeQuietly(metrics, \"pluginMetrics\", firstException));\n        Throwable throwable = firstException.get();\n        if (throwable != null) throw new KafkaException(\"failed closing plugin\", throwable);\n    }\n}\n","sourceCodeStart":112,"sourceCodeEnd":133,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/internals/Plugin.java#L112-L133","documentation":"Thrown by Plugin.close() when shutting down a wrapped plugin instance (or its associated pluginMetrics) raised an exception during AutoCloseable.close(). The Kafka Plugin wrapper closes the user-supplied instance and its metrics in sequence, captures the first failure into an AtomicReference, and rethrows it wrapped as a KafkaException so resource cleanup failures are not swallowed silently. The original cause is always attached as the exception's cause, so the real failure (e.g. a connector/serializer refusing close) is inspectable via getCause().","triggerScenarios":"Calling close() (directly or via try-with-resources) on a Plugin<T> whose wrapped instance is an AutoCloseable that throws from its close() method, or whose pluginMetrics Metrics object fails to close. Most commonly hit when the producer/consumer/connect runtime tears down a configured plugin such as a custom Serializer, Partitioner, ProducerInterceptor, ConsumerInterceptor, or Connect Converter/Transformation that misbehaves on shutdown.","commonSituations":"A custom plugin holds an open network connection, file handle, or thread pool and throws on close; a Connect connector worker is stopped while a task is mid-flight; a serializer backed by an external service fails to close its client; running with a buggy third-party plugin whose close() is not idempotent; version mismatch where a plugin compiled against an older client API throws a NoSuchMethodError during close.","solutions":["Inspect the attached Throwable cause (KafkaException#getCause) — the stack trace identifies the exact class and line in the plugin that failed to close.","Fix the offending plugin's close() to be idempotent and swallow/label expected shutdown errors (e.g. AlreadyClosedException) rather than propagating them.","Ensure any resources the plugin opens (sockets, executors, files) are tracked and released in close() in reverse acquisition order, inside their own try/finally.","If using Connect, verify the connector/plugin version is compatible with the broker/client runtime and update to a matching release.","If the failure is non-fatal to your shutdown path, catch KafkaException at the call site, log the cause, and continue teardown rather than letting it abort the whole shutdown."],"exampleFix":"// before — plugin close() propagates failure\nclass MySerializer implements Serializer<byte[]>, AutoCloseable {\n    public void close() {\n        httpStream.close(); // throws if peer already reset the connection\n    }\n}\n\n// after — close() is idempotent and never throws on shutdown\nprivate final AtomicBoolean closed = new AtomicBoolean();\npublic void close() {\n    if (!closed.compareAndSet(false, true)) return;\n    try { httpStream.close(); }\n    catch (java.io.IOException e) { log.warn(\"Error closing http stream on shutdown\", e); }\n}","handlingStrategy":"try-catch","validationCode":"// Before closing, confirm the wrapped instance is in a closeable state.\nPlugin<?> plugin = ...;\nObject inner = plugin.get();\nif (inner instanceof AutoCloseable) {\n    // surface any pre-close state (e.g. stop producers/consumers first)\n    // so close() is unlikely to throw.\n}","typeGuard":"// Ensure the wrapped instance advertises AutoCloseable before relying on close().\nstatic boolean isSafelyCloseable(Plugin<?> p) {\n    return p.get() instanceof AutoCloseable;\n}","tryCatchPattern":"try {\n    plugin.close();\n} catch (KafkaException ke) {\n    // Plugin.close() bundles the underlying cause; log and continue shutdown.\n    log.warn(\"Plugin {} did not close cleanly: {}\", plugin.get().getClass().getSimpleName(), ke.getCause());\n} catch (Exception e) {\n    // close() declares 'throws Exception'; swallow non-Kafka exceptions during shutdown.\n    log.warn(\"Unexpected error closing plugin\", e);\n}","preventionTips":["Stop/flush any producers, consumers, or admin clients held by the wrapped instance before calling plugin.close() so its own close() does not throw.","Treat plugin close failures as non-fatal during shutdown: log and proceed so one bad plugin does not abort cleanup of others.","If you hold a List<Plugin<?>>, close each in its own try-catch so a failure on one does not skip the rest.","Always inspect ke.getCause() rather than the wrapper message to find the real failure (e.g. InterruptedIOException, leaked buffer)."],"tags":["kafka-clients","plugins","resource-lifecycle","shutdown"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}