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

  1. Set the MetricsContext namespace before any call to Metrics.addMetric / addSensor or before constructing the JmxReporter-backed Metrics instance.
  2. If the namespace is only known late, construct the JmxReporter and Metrics lazily once the namespace is available rather than mutating an existing reporter.
  3. Avoid mixing the deprecated JmxReporter(String) prefix constructor with contextChange; rely on MetricsContext only.
  4. 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

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


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