apache/kafka · error · IllegalStateException
JMX MetricsContext can only be updated before JMX metrics ar
Error message
JMX MetricsContext can only be updated before JMX metrics are created
What it means
Thrown by JmxReporter.contextChange(MetricsContext) when the namespace is changed after at least one MBean has already been registered. The JMX namespace/prefix becomes part of every registered MBean's ObjectName, so changing it post-registration would create orphaned or duplicated MBeans. Kafka therefore mandates that the MetricsContext be finalised before any metric is added to the Metrics instance.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/metrics/JmxReporter.java:327
try {
Pattern includePattern = Pattern.compile(include);
Pattern excludePattern = Pattern.compile(exclude);
return s -> includePattern.matcher(s).matches()
&& !excludePattern.matcher(s).matches();
} catch (PatternSyntaxException e) {
throw new ConfigException("JMX filter for configuration" + METRICS_CONFIG_PREFIX
+ ".(include/exclude) is not a valid regular expression");
}
}
@Override
public void contextChange(MetricsContext metricsContext) {
String namespace = metricsContext.contextLabels().get(MetricsContext.NAMESPACE);
Objects.requireNonNull(namespace);
synchronized (LOCK) {
if (!mbeans.isEmpty()) {
throw new IllegalStateException("JMX MetricsContext can only be updated before JMX metrics are created");
}
// prevent prefix from getting reset back to empty for backwards compatibility
// with the deprecated JmxReporter(String prefix) constructor, in case contextChange gets called
// via one of the Metrics() constructor with a default empty MetricsContext()
if (namespace.isEmpty()) {
return;
}
prefix = namespace;
}
}
}
View on GitHub (pinned to c31c9215e1)
Solutions
- Set the MetricsContext namespace before any call to Metrics.addMetric / addSensor or before constructing the JmxReporter-backed Metrics instance.
- If the namespace is only known late, construct the JmxReporter and Metrics lazily once the namespace is available rather than mutating an existing reporter.
- Avoid mixing the deprecated JmxReporter(String) prefix constructor with contextChange; rely on MetricsContext only.
- Reorder startup so contextChange is the first call on the reporter, before any metric registration.
Example fix
// before:
Metrics m = new Metrics();
m.addSensor("s"); // registers an mbean
m.contextChange(new MetricsContext().contextLabels(Map.of(MetricsContext.NAMESPACE, "tenantA")));
// after:
Metrics m = new Metrics();
m.contextChange(new MetricsContext().contextLabels(Map.of(MetricsContext.NAMESPACE, "tenantA")));
m.addSensor("s"); Defensive patterns
Strategy: validation
Validate before calling
// Set MetricsContext BEFORE registering any metric/sensor.
Metrics metrics = new Metrics(config);
MetricsContext ctx = new KafkaMetricsContext("my-namespace");
// IMPORTANT: do this first, when no sensors have been added.
assert metrics.metrics().isEmpty() : "set context before adding metrics";
((JmxReporter) metricsReporter).contextChange(ctx); // mbeans must be empty here
// only now: metrics.addSensor(...) / addMetric(...) Type guard
// Narrow to the pre-metrics-creation window.
boolean canUpdateMetricsContext(JmxReporter r) {
return r != null; // safe only if no metric has been added since reporter init
} Try / catch
try {
jmxReporter.contextChange(ctx);
} catch (IllegalStateException e) {
// mbeans already populated; recreate the Metrics + reporter in the right order.
} Prevention
- Call contextChange (or KafkaMetricsContext setup) at Metrics construction, before any addSensor/addMetric.
- Never swap namespaces after producers/consumers/clients have started reporting.
- If namespace must change, tear down and rebuild the Metrics instance and its reporters.
- Treat MetricsContext as immutable-after-first-metric configuration.
When it happens
Trigger: Code obtains a Metrics instance, registers one or more sensors/metrics (which causes JmxReporter to add an entry to its mbeans map), then later calls metricsContext.contextChange(...) or Metrics.addReporter(JmxReporter) after the fact. The check `if (!mbeans.isEmpty())` at JmxReporter.java:326 fails and IllegalStateException is thrown.
Common situations: Custom code that constructs Metrics with a default MetricsContext and then tries to set the namespace after registering metrics. Embedding the Kafka producer/consumer in a framework that initialises metrics eagerly but assigns a tenant namespace lazily. Mixing the deprecated JmxReporter(String prefix) constructor with later contextChange calls.
Related errors
- Error creating mbean attribute for metricName :{metricName}
- JMX filter for configuration{metricsConfigPrefix}.(include/e
- Error unregistering mbean
- Error registering mbean {mbeanName}
- Could not find attribute {name}
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/1319330f497ded58.json.
Report an issue: GitHub.