nathanmarz/storm · error · RuntimeException
The same metric name
Error message
The same metric name `${name}` was registered twice. What it means
registerMetric stores metrics per time-bucket and per task id, and each metric name must be unique within a task. Registering two metrics with the same name for the same task throws this RuntimeException to prevent silent metric collision/overwriting in the metrics consumers.
Solutions
- Use unique, descriptive names for every metric (e.g. prefix with component/purpose).
- Guard registration with a flag or check so it executes exactly once per task.
- Differentiate metrics registered in loops by appending the loop key to the name.
- On re-prepare (e.g. after reload), skip registration if the metric already exists — reuse the instance.
Example fix
// before
context.registerMetric("count", new CountMetric(), 10);
// later, same task:
context.registerMetric("count", new CountMetric(), 10); // throws
// after
context.registerMetric("spout-emit-count", new CountMetric(), 10);
context.registerMetric("bolt-ack-count", new CountMetric(), 10); Defensive patterns
Strategy: validation
Validate before calling
Set<String> registered = new HashSet<>();
boolean safe = registered.add("my-metric-name"); // only call registerMetric if safe==true Try / catch
try {
context.registerMetric(name, metric, interval);
} catch (RuntimeException e) {
if (e.getMessage().contains("registered twice")) {
LOG.warn("metric {} already registered; reusing", name);
} else { throw e; }
} Prevention
- Keep a single registration method per component that runs once.
- Prefix metric names with component/role to guarantee uniqueness.
- Never register metrics inside loops without appending a unique key.
- Track registered names in a Set and check before calling registerMetric.
When it happens
Trigger: Calling registerMetric twice with the same name string within the same task — e.g. registering "request-count" in both prepare and a helper method, or in a loop that re-registers on each execution.
Common situations: Copy-pasted metric registration code with duplicated names, metrics registered once per element/component in a loop when they should be registered once, re-preparing bolts with shared registration logic that isn't idempotent.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Could not instantiate a class listed in config under section
- A single worker should have 1 SystemBolt instance.
- Non-system tuples should never be sent to __system bolt.
- MeanReducer::reduce called with unsupported input type
- TopologyContext.registerMetric can only be called from…
AI-assisted analysis of nathanmarz/storm@cdb116e942 (2026-09-12).
Data as JSON: /api/errors/9260ad2ea71344bd.
Report an issue: GitHub.
Appendix: source
Thrown at storm-core/src/jvm/backtype/storm/task/TopologyContext.java:246
public <T extends IMetric> T registerMetric(String name, T metric, int timeBucketSizeInSecs) {
if((Boolean)_openOrPrepareWasCalled.deref() == true) {
throw new RuntimeException("TopologyContext.registerMetric can only be called from within overridden " +
"IBolt::prepare() or ISpout::open() method.");
}
Map m1 = _registeredMetrics;
if(!m1.containsKey(timeBucketSizeInSecs)) {
m1.put(timeBucketSizeInSecs, new HashMap());
}
Map m2 = (Map)m1.get(timeBucketSizeInSecs);
if(!m2.containsKey(_taskId)) {
m2.put(_taskId, new HashMap());
}
Map m3 = (Map)m2.get(_taskId);
if(m3.containsKey(name)) {
throw new RuntimeException("The same metric name `" + name + "` was registered twice." );
} else {
m3.put(name, metric);
}
return metric;
}
/*
* Convinience method for registering ReducedMetric.
*/
public ReducedMetric registerMetric(String name, IReducer reducer, int timeBucketSizeInSecs) {
return registerMetric(name, new ReducedMetric(reducer), timeBucketSizeInSecs);
}
/*
* Convinience method for registering CombinedMetric.
*/
public CombinedMetric registerMetric(String name, ICombiner combiner, int timeBucketSizeInSecs) {
return registerMetric(name, new CombinedMetric(combiner), timeBucketSizeInSecs);View on GitHub (pinned to cdb116e942)