apache/beam · error
metric name %s being reused for a different metric type in a
Error message
metric name %s being reused for a different metric type in a single PTransform
What it means
The metric Store indexes user metrics per PTransform by labels (transform, namespace, name). If the same metric name is registered twice within one PTransform but with a different metric kind (counter vs distribution vs gauge), the store cannot represent both, so it panics to preserve the internal invariant.
Source
Thrown at sdks/go/pkg/beam/core/metrics/store.go:244
transitions *int64
bundleState *BundleState
}
func newStore() *Store {
return &Store{store: make(map[Labels]userMetric), stateRegistry: make(map[string]*[4]ExecutionState), transitions: new(int64), bundleState: &BundleState{}}
}
// storeMetric stores a metric away on its first use so it may be retrieved later on.
// In the event of a name collision, storeMetric can panic, so it's prudent to release
// locks if they are no longer required.
func (b *Store) storeMetric(pid string, n name, m userMetric) {
b.mu.Lock()
defer b.mu.Unlock()
l := Labels{transform: pid, namespace: n.namespace, name: n.name}
if ms, ok := b.store[l]; ok {
if ms.kind() != m.kind() {
panic(fmt.Sprintf("metric name %s being reused for a different metric type in a single PTransform", n))
}
return
}
b.store[l] = m
}
// BundleState returns the bundle state.
func (b *Store) BundleState() string {
bs := *(*BundleState)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&b.bundleState))))
return bs.String()
}
// StateRegistry returns the state registry that stores bundleID to executions states mapping.
func (b *Store) StateRegistry() string {
b.mu.Lock()
defer b.mu.Unlock()
builder := &strings.Builder{}
builder.WriteString("\n | All Bundle Process States | \n")View on GitHub (pinned to 12126d8942)
Solutions
- Rename one of the conflicting metrics so each name maps to exactly one kind per transform.
- Move one of the metrics into a different namespace.
- Keep metric kind consistent per name across the codebase (e.g. a metrics registry/constant list).
Example fix
// before NewCounter(ns, "records"), NewDistribution(ns, "records") // panic // after NewCounter(ns, "records"), NewDistribution(ns, "records_values")
Defensive patterns
Strategy: validation
Validate before calling
if seen[kindOf(m)] {
return fmt.Errorf("metric %s.%s already registered as a different kind", ns, name)
} Prevention
- Keep a central registry mapping metric name to kind and reuse it everywhere.
- Never change a metric's kind without renaming it.
- In shared libraries, prefix metric names with the library namespace to avoid collisions.
When it happens
Trigger: Within a single PTransform, calling e.g. metrics.NewCounter(ns, "x") and metrics.NewDistribution(ns, "x") (or gauge) with the same namespace/name — the second storeMetric call finds an existing entry whose kind differs.
Common situations: Refactoring a metric's type without renaming it, two libraries used in one transform coincidentally sharing a metric name, or copy-pasting metric declarations and changing only the constructor kind.
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
- invalid bundle processing state: %d
- namespace and name are required to be non-empty, got %q and
- failed to deduce Step from MonitoringInfo: %v
- panic in ElementManager.Bundles watermark evaluation gorouti
- pipeline panicked: %v Stacktrace: %s
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/3a599144a404744c.
Report an issue: GitHub.