grpc/grpc-java · error · IllegalStateException
Metric with name ${name} already exists
Error message
Metric with name ${name} already exists What it means
MetricInstrumentRegistry.registerDoubleCounter registers a double counter metric instrument by unique name in a process-wide registry. Registering the same name twice throws IllegalStateException('Metric with name X already exists') to prevent conflicting definitions (labels/units) for one metric name.
Source
Thrown at api/src/main/java/io/grpc/MetricInstrumentRegistry.java:92
* @param description a description of the metric
* @param unit the unit of measurement for the metric
* @param requiredLabelKeys a list of required label keys
* @param optionalLabelKeys a list of optional label keys
* @param enableByDefault whether the metric should be enabled by default
* @return the newly created DoubleCounterMetricInstrument
* @throws IllegalStateException if a metric with the same name already exists
*/
public DoubleCounterMetricInstrument registerDoubleCounter(String name,
String description, String unit, List<String> requiredLabelKeys,
List<String> optionalLabelKeys, boolean enableByDefault) {
checkArgument(!Strings.isNullOrEmpty(name), "missing metric name");
checkNotNull(description, "description");
checkNotNull(unit, "unit");
checkNotNull(requiredLabelKeys, "requiredLabelKeys");
checkNotNull(optionalLabelKeys, "optionalLabelKeys");
synchronized (lock) {
if (registeredMetricNames.contains(name)) {
throw new IllegalStateException("Metric with name " + name + " already exists");
}
int index = nextAvailableMetricIndex;
if (index + 1 == metricInstruments.length) {
resizeMetricInstruments();
}
// TODO(dnvindhya): add limit for number of optional labels allowed
DoubleCounterMetricInstrument instrument = new DoubleCounterMetricInstrument(
index, name, description, unit, requiredLabelKeys, optionalLabelKeys,
enableByDefault);
metricInstruments[index] = instrument;
registeredMetricNames.add(name);
nextAvailableMetricIndex += 1;
return instrument;
}
}
/**
* Registers a new Long Counter metric instrument.View on GitHub (pinned to 64daddc1f3)
Solutions
- Guard registration so it runs once per JVM (static init flag, singleton init)
- Check name existence before registering, or dedupe metric names across libraries
- In app servers, ensure the registry lifecycle is tied to the webapp classloader
- Rename the metric if a genuine collision with another library exists
Example fix
// before
registry.registerDoubleCounter("rpc.duration", desc, unit, labels, optionalLabels);
// after
if (!registered) {
registry.registerDoubleCounter("rpc.duration", desc, unit, labels, optionalLabels);
registered = true;
} Defensive patterns
Strategy: try-catch
Validate before calling
// no public pre-check API in older versions; guard at call site
if (alreadyRegistered("rpc.duration")) return; // track your own registration set Try / catch
try {
registry.registerDoubleCounter(name, desc, unit, labelKeys, optionalLabelKeys);
} catch (IllegalStateException e) {
// duplicate metric name: log and continue if definition is identical
} Prevention
- Initialize metrics once per process via a singleton or static initializer
- Deduplicate metric names across libraries you integrate
- Tie metric registration to classloader/app lifecycle in containers and app servers
- Keep grpc versions aligned so built-in instruments are registered exactly once
When it happens
Trigger: Calling registerDoubleCounter twice with the same metric name — typically during repeated SDK/library initialization, multiple registry instances sharing names, or classloader re-initialization within one JVM.
Common situations: Calling grpc initialization code twice (e.g. both app code and a framework plugin init metrics); hot redeploys in an app server that does not release the old registry; two versions of a library both registering the same metric name.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- ScheduledExecutorService not set in Builder
- ChannelLogger is not set in Builder
- NameResolverRegistry is not set in Builder
- No method bound for descriptor entry ${fullMethodName}
- Framer already closed
AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08).
Data as JSON: /api/errors/5ce0a87572533dfe.
Report an issue: GitHub.