tsenart/vegeta · error

failed to register metric %v: %w

Error message

failed to register metric %v: %w

What it means

Returned when one of the prometheus collectors (latency histogram, bytes-in/out counters, fail counter) cannot be registered with the provided prometheus.Registerer. Prometheus rejects duplicate registrations of the same metric name, so a second Register call on the same registry fails.

Source

Thrown at lib/prom/prom.go:57

			Help: "Bytes sent to servers during requests",
		}, baseLabels),
		requestFailCounter: prometheus.NewCounterVec(prometheus.CounterOpts{
			Name: "request_fail_count",
			Help: "Count of failed requests",
		}, append(baseLabels[:len(baseLabels):len(baseLabels)], "message")),
	}
}

// Register registers all Prometheus metrics in r.
func (pm *Metrics) Register(r prometheus.Registerer) error {
	for _, c := range []prometheus.Collector{
		pm.requestLatencyHistogram,
		pm.requestBytesInCounter,
		pm.requestBytesOutCounter,
		pm.requestFailCounter,
	} {
		if err := r.Register(c); err != nil {
			return fmt.Errorf("failed to register metric %v: %w", c, err)
		}
	}
	return nil
}

// Observe metrics given a vegeta.Result.
func (pm *Metrics) Observe(res *vegeta.Result) {
	code := strconv.FormatUint(uint64(res.Code), 10)
	pm.requestBytesInCounter.WithLabelValues(res.Method, res.URL, code).Add(float64(res.BytesIn))
	pm.requestBytesOutCounter.WithLabelValues(res.Method, res.URL, code).Add(float64(res.BytesOut))
	pm.requestLatencyHistogram.WithLabelValues(res.Method, res.URL, code).Observe(res.Latency.Seconds())
	if res.Error != "" {
		pm.requestFailCounter.WithLabelValues(res.Method, res.URL, code, res.Error)
	}
}

// NewHandler returns a new http.Handler that exposes Prometheus
// metrics registed in r in the OpenMetrics format.

View on GitHub (pinned to cf58112690)

Solutions

  1. Use prometheus.NewRegistry() per attack or component instead of the default global registry.
  2. Call the Unregister (or a wrapped MustUnregister) before re-Registering the same collectors.
  3. Guard registration with promauto or check AlreadyRegisteredError before Registering.
  4. Upgrade vegeta if you hit duplicate-name collisions between its built-in collectors and your own.

Example fix

// before
reg := prometheus.DefaultRegisterer
if err := prom.Register(reg); err != nil { return err }
// after
reg := prometheus.NewRegistry()
if err := prom.Register(reg); err != nil { return err }
Defensive patterns

Strategy: try-catch

Validate before calling

// Check for existing registration before registering vegeta metrics
func safeRegister(r prometheus.Registerer, c prometheus.Collector) error {
    if err := r.Register(c); err != nil {
        var are prometheus.AlreadyRegisteredError
        if errors.As(err, &are) {
            return nil // already registered, reuse
        }
        return err
    }
    return nil
}

Type guard

func isAlreadyRegistered(err error) bool {
    var are prometheus.AlreadyRegisteredError
    return errors.As(err, &are)
}

Try / catch

if err := prom.Register(reg); err != nil {
    var are prometheus.AlreadyRegisteredError
    if errors.As(err, &are) {
        return nil // safe to ignore: metric exists
    }
    return fmt.Errorf("metric registration failed: %w", err)
}

Prevention

When it happens

Trigger: Calling prom.Registerer's Register (e.g. via attack metric registration) twice on the same prometheus.Registry, or the same registry already containing metrics with identical names.

Common situations: Registering vegeta attack metrics into prometheus.DefaultRegisterer in a long-running process, then re-registering (e.g. on config reload or a second attack); hot-reloading metrics without deregistering.

Related errors


AI-assisted analysis of tsenart/vegeta@cf58112690 (2026-08-31). Data as JSON: /api/errors/a1498ab7cc5c7498. Report an issue: GitHub.