apache/kafka · error · IllegalStateException

This PluginMetrics instance is closed

Error message

This PluginMetrics instance is closed

What it means

Thrown by PluginMetricsImpl.metricName(...) after the instance has been closed (the close() method set closing=true). PluginMetrics is a scoped, closeable metrics container handed to plugins; once closed it must not be used because its backing metrics and sensors have been deregistered. The IllegalStateException signals use-after-close rather than a recoverable runtime condition.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/metrics/internals/PluginMetricsImpl.java:49

public class PluginMetricsImpl implements PluginMetrics, Closeable {

    private static final String GROUP = "plugins";

    private final Metrics metrics;
    private final Map<String, String> tags;
    private final Set<MetricName> metricNames = ConcurrentHashMap.newKeySet();
    private final Set<String> sensors = ConcurrentHashMap.newKeySet();
    private volatile boolean closing = false;

    public PluginMetricsImpl(Metrics metrics, Map<String, String> tags) {
        this.metrics = metrics;
        this.tags = tags;
    }

    @Override
    public MetricName metricName(String name, String description, LinkedHashMap<String, String> tags) {
        if (closing) throw new IllegalStateException("This PluginMetrics instance is closed");
        for (String tagName : tags.keySet()) {
            if (this.tags.containsKey(tagName)) {
                throw new IllegalArgumentException("Cannot use " + tagName + " as a tag name");
            }
        }
        Map<String, String> metricsTags = new LinkedHashMap<>(this.tags);
        metricsTags.putAll(tags);
        return metrics.metricName(name, GROUP, description, metricsTags);
    }

    @Override
    public void addMetric(MetricName metricName, MetricValueProvider<?> metricValueProvider) {
        if (closing) throw new IllegalStateException("This PluginMetrics instance is closed");
        if (metricNames.contains(metricName)) {
            throw new IllegalArgumentException("Metric " + metricName + " already exists");
        }
        metrics.addMetric(metricName, metricValueProvider);
        metricNames.add(metricName);

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Stop all metric-producing threads/tasks (or null out their PluginMetrics reference) before pluginMetrics.close() returns.
  2. Treat PluginMetrics as owned by the plugin lifecycle: do not store it in static fields; obtain it fresh per task/lifecycle.
  3. Add a closing/active guard in the plugin's record path and skip metric updates once closed.
  4. In tests, ensure close() is the last operation on the PluginMetrics instance, or use a fresh instance per test method.

Example fix

// before
// plugin close() runs first, then a background thread:
metrics.metricName("rx", "desc", tags); // throws

// after
@Override public void close() {
    backgroundThread.interrupt();
    backgroundThread.join();
    pluginMetrics.close();
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    MetricName name = pluginMetrics.metricName(n, description, tags);
} catch (IllegalStateException e) {
    // PluginMetrics instance has been closed by the framework; stop emitting from this plugin
    log.debug("PluginMetrics closed, skipping metricName creation", e);
}

Prevention

When it happens

Trigger: Calling pluginMetrics.metricName(name, description, tags) after pluginMetrics.close() has run. Happens when plugin code caches the PluginMetrics instance and uses it from an async callback, scheduled task, or shutdown hook that runs after the plugin's close() lifecycle method.

Common situations: Connector/agent plugin that records metrics from a background thread not joined before close(); a metrics reference leaked to a static field or cache that outlives the plugin; shutdown ordering bug where the runtime closes PluginMetrics before the plugin's own cleanup; tests that close the metrics instance then continue assertions.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/ff0543ab2c8c67f8.json. Report an issue: GitHub.