apache/kafka · error · IllegalArgumentException

A metric named '{metricName}' already exists, can't register

Error message

A metric named '{metricName}' already exists, can't register another one.

What it means

Thrown by Metrics.addMetric when a KafkaMetric with an equal MetricName is already registered in the same Metrics instance. MetricName equality is determined by group, name, and tags, so a second addMetric call with the same triple collides. Kafka treats metric identity as unique per Metrics registry because reporters (JMX, etc.) key off the MetricName and duplicate registration would produce ambiguous values.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java:520

    /**
     * Add a metric to monitor an object that implements MetricValueProvider. This metric won't be associated with any
     * sensor. This is a way to expose existing values as metrics. User is expected to add any additional
     * synchronization to update and access metric values, if required.
     *
     * @param metricName The name of the metric
     * @param metricValueProvider The metric value provider associated with this metric
     * @throws IllegalArgumentException if a metric with same name already exists.
     */
    public void addMetric(MetricName metricName, MetricConfig config, MetricValueProvider<?> metricValueProvider) {
        KafkaMetric m = new KafkaMetric(new Object(),
                                        Objects.requireNonNull(metricName),
                                        Objects.requireNonNull(metricValueProvider),
                                        config == null ? this.config : config,
                                        time);
        KafkaMetric existingMetric = registerMetric(m);
        if (existingMetric != null) {
            throw new IllegalArgumentException("A metric named '" + metricName + "' already exists, can't register another one.");
        }
    }

    /**
     * Add a metric to monitor an object that implements MetricValueProvider. This metric won't be associated with any
     * sensor. This is a way to expose existing values as metrics. User is expected to add any additional
     * synchronization to update and access metric values, if required.
     *
     * @param metricName The name of the metric
     * @param metricValueProvider The metric value provider associated with this metric
     */
    public void addMetric(MetricName metricName, MetricValueProvider<?> metricValueProvider) {
        addMetric(metricName, null, metricValueProvider);
    }

    /**
     * Create or get an existing metric to monitor an object that implements MetricValueProvider.
     * This metric won't be associated with any sensor. This is a way to expose existing values as metrics.

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Use Metrics.addMetricIfAbsent(name, config, provider) if idempotent registration is desired.
  2. Before adding, call Metrics.metric(name) or Metrics.metrics().get(name); if non-null, removeMetric(name) or skip.
  3. Ensure the MetricName tags actually distinguish logically separate metrics (e.g. include node-id, topic, partition).
  4. Tear down metrics in close()/close() of the owning component so re-init does not collide.

Example fix

// before: metrics.addMetric(name, provider); // throws if already registered
// after:  metrics.addMetricIfAbsent(name, null, provider);
Defensive patterns

Strategy: validation

Validate before calling

// Check the live metrics map before registering a new one.
MetricName name = metrics.metricName("rate", "grp", "desc", tags);
if (metrics.metrics().containsKey(name)) {
    // already registered; reuse or skip instead of re-adding.
    return metrics.metrics().get(name);
}
metrics.addMetric(name, provider);

Type guard

// Narrow to not-already-registered before calling addMetric.
boolean isUnregistered(Metrics metrics, MetricName name) {
    return metrics != null && name != null && !metrics.metrics().containsKey(name);
}

Try / catch

try {
    metrics.addMetric(metricName, provider);
} catch (IllegalArgumentException e) {
    // collision: log and reuse the existing metric from metrics.metrics().get(metricName)
}

Prevention

When it happens

Trigger: Code calls Metrics.addMetric(name, provider) twice with the same MetricName, or adds a metric whose name collides with one already registered by addSensor/addMetric earlier. registerMetric returns the existing metric and addMetric at line 519-520 throws IllegalArgumentException naming the MetricName.

Common situations: Plugin code that re-initialises metrics on reconnect without first removing the old ones. Multi-tenant setups where tags intended to differentiate metrics are missing or constant. Frameworks (Connect, Streams) wrapping the producer and re-adding metrics on task recreation. Calling addMetric inside a loop with reused tag maps.

Related errors


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