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
- Stop all metric-producing threads/tasks (or null out their PluginMetrics reference) before pluginMetrics.close() returns.
- Treat PluginMetrics as owned by the plugin lifecycle: do not store it in static fields; obtain it fresh per task/lifecycle.
- Add a closing/active guard in the plugin's record path and skip metric updates once closed.
- 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
- Treat PluginMetrics as framework-owned; never call it after your plugin's close() returns.
- Track an 'active' flag in your plugin and short-circuit all metric calls once inactive.
- Create all metric names eagerly during plugin init(), not lazily at runtime.
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
- This consumer has already been closed.
- JMX MetricsContext can only be updated before JMX metrics ar
- Cannot use {tagName} as a tag name
- Metric {metricName} already exists
- NetworkClient is no longer active, state is {state}
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/ff0543ab2c8c67f8.json.
Report an issue: GitHub.