{"id":"d9f90628e56660df","repo":"apache/kafka","slug":"a-metric-named-metricname-already-exists-can-d9f906","errorCode":null,"errorMessage":"A metric named '{metricName}' already exists, can't register another one.","messagePattern":"A metric named '(.+?)' already exists, can't register another one\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/metrics/Sensor.java","lineNumber":304,"sourceCode":"     * Register a compound statistic with this sensor which yields multiple measurable quantities (like a histogram)\n     * @param stat The stat to register\n     * @param config The configuration for this stat. If null then the stat will use the default configuration for this\n     *        sensor.\n     * @return true if stat is added to sensor, false if sensor is expired\n     */\n    public synchronized boolean add(CompoundStat stat, MetricConfig config) {\n        if (hasExpired())\n            return false;\n\n        final MetricConfig statConfig = config == null ? this.config : config;\n        stats.add(new StatAndConfig(Objects.requireNonNull(stat), () -> statConfig));\n        Object lock = metricLock();\n        for (NamedMeasurable m : stat.stats()) {\n            final KafkaMetric metric = new KafkaMetric(lock, m.name(), m.stat(), statConfig, time);\n            if (!metrics.containsKey(metric.metricName())) {\n                KafkaMetric existingMetric = registry.registerMetric(metric);\n                if (existingMetric != null) {\n                    throw new IllegalArgumentException(\"A metric named '\" + metric.metricName() + \"' already exists, can't register another one.\");\n                }\n                metrics.put(metric.metricName(), metric);\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Register a metric with this sensor\n     * @param metricName The name of the metric\n     * @param stat The statistic to keep\n     * @return true if metric is added to sensor, false if sensor is expired\n     */\n    public boolean add(MetricName metricName, MeasurableStat stat) {\n        return add(metricName, stat, null);\n    }\n\n    /**","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/metrics/Sensor.java#L286-L322","documentation":"Thrown by Sensor.add(CompoundStat stat, MetricConfig config) when registry.registerMetric returns a non-null existing metric for one of the NamedMeasurable produced by the compound stat. The global Metrics registry keys metrics by MetricName (group+name+tags), so a collision means two different stats are trying to publish the same metric identity. It surfaces as an IllegalArgumentException to prevent silent overwriting of an existing KafkaMetric.","triggerScenarios":"Calling sensor.add(compoundStat) (e.g. a Histogram, Percentiles, or any CompoundStat emitting multiple NamedMeasurables) where at least one of stat.stats() yields a MetricName already registered in the same Metrics instance. Common when two sensors add the same compound stat type with identical name/tags, or when a sensor is recreated after the prior metric was not removed.","commonSituations":"Plugin or connector code that registers a Histogram/Percentiles per task without including a unique tag (task id, partition) in the metric name; re-registration after a restart where the previous sensor's metric lingers; shared Metrics instance across components that happen to pick the same metric name; tests reusing a static Metrics without cleanup.","solutions":["Make the metric name unique by adding distinguishing tags (e.g. client-id, task-id, partition) when constructing the CompoundStat's NamedMeasurable names.","Ensure the prior sensor is removed via Metrics.removeSensor(...) and that its metrics are deregistered before re-adding.","Check sensor metrics()/registry for the name before adding, or guard registration with metrics.containsKey(name).","Use a dedicated Metrics instance (or PluginMetrics scope) per component so unrelated metrics cannot collide on the same name."],"exampleFix":"// before\nSensor s = metrics.sensor(\"latency\");\ns.add(new Percentiles(100, Percentiles.BUCKET_100_LATENCIES));\n// re-run: name \"latency-percentiles\" already registered -> exception\n\n// after\nSensor s = metrics.sensor(\"latency-\" + taskId);\ns.add(new Percentiles(100, Percentiles.BUCKET_100_LATENCIES));\n// unique per task; or metrics.removeSensor(\"latency\") before re-adding","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    sensor.add(compoundStat, config);\n} catch (IllegalArgumentException e) {\n    // one of this CompoundStat's NamedMeasurables collides with an existing metric name\n    log.warn(\"Skipping compound stat {} — metric already registered\", compoundStat, e);\n}","preventionTips":["Use globally-unique metric names derived from a stable namespace prefix.","Register each CompoundStat's measurables once at startup, not per request or per connection.","Avoid adding the same CompoundStat type to multiple sensors that share a Metrics registry."],"tags":["metrics","sensor","duplicate-registration","compound-stat"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}