argoproj/argo-workflows · error
help for metric %s is already set to %s, it cannot be change
Error message
help for metric %s is already set to %s, it cannot be changed
What it means
When a workflow (or a retry/resubmit of it) re-declares a Prometheus custom metric whose name already exists, matchExistingMetric verifies the new spec is consistent with the already-registered OpenTelemetry instrument. If metricSpec.Help differs from the existing instrument's description, the help text of a registered metric cannot be changed, so the executor fails the metric (and the workflow is marked Error).
Source
Thrown at workflow/metrics/metrics_custom.go:144
// GetCustomMetric returns a custom (or any) metric from it's key
// This is exported for legacy testing only
func (m *Metrics) GetCustomMetric(key string) *telemetry.Instrument {
// It's okay to return nil metrics in this function
return m.GetInstrument(key)
}
// CustomMetricExists returns if metric exists from its key
// This is exported for testing only
func (m *Metrics) CustomMetricExists(key string) bool {
return m.GetCustomMetric(key) != nil
}
// TODO labels on custom metrics
func (m *Metrics) matchExistingMetric(metricSpec *wfv1.Prometheus) (*telemetry.Instrument, error) {
key := metricSpec.Name
if inst := m.GetInstrument(key); inst != nil {
if inst.GetDescription() != metricSpec.Help {
return nil, fmt.Errorf("help for metric %s is already set to %s, it cannot be changed", metricSpec.Name, inst.GetDescription())
}
wantedType := metricSpec.GetMetricType()
switch inst.GetOtel().(type) {
case *metric.Float64ObservableGauge:
if wantedType != wfv1.MetricTypeGauge && !metricSpec.IsRealtime() {
return nil, fmt.Errorf("found existing gauge for custom metric %s of type %s", metricSpec.Name, wantedType)
}
case *metric.Float64ObservableCounter:
if wantedType != wfv1.MetricTypeCounter {
return nil, fmt.Errorf("found existing counter for custom metric %s of type %s", metricSpec.Name, wantedType)
}
case *metric.Float64Histogram:
if wantedType != wfv1.MetricTypeHistogram {
return nil, fmt.Errorf("found existing histogram for custom metric %s of type %s", metricSpec.Name, wantedType)
}
default:
return nil, fmt.Errorf("found unwanted type %s for custom metric %s of type %s", reflect.TypeOf(inst.GetOtel()), metricSpec.Name, wantedType)
}View on GitHub (pinned to 35bff19146)
Solutions
- Give each distinct help text a distinct metric name
- Make the help string identical across all declarations of the same metric name (including retries of prior runs)
- If the change was intentional, run in a fresh executor pod / workflow name so the instrument is created anew
- Check metric help templating (e.g. {{workflow.name}}) isn't producing varying strings
Example fix
// before - name: duration help: How long the step took - name: duration help: Step duration # mismatch // after - name: duration help: How long the step took - name: duration_steps help: Step duration
Defensive patterns
Strategy: validation
Validate before calling
// ensure help text is constant per metric name across the spec
helpByName := map[string]string{}
for _, m := range spec.Prometheus {
if h, seen := helpByName[m.Name]; seen && h != m.Help {
return fmt.Errorf("metric %s has conflicting help: %q vs %q", m.Name, h, m.Help)
}
helpByName[m.Name] = m.Help
} Try / catch
if err != nil && strings.Contains(err.Error(), "is already set to") {
// fix spec help/name mismatch and resubmit as a new workflow
} Prevention
- Never vary `help` for the same metric name, including across retries/resubmits
- Use unique metric names for metrics with different descriptions
- Beware templated help strings that resolve differently per step
- Run `argo lint` before submit
When it happens
Trigger: A workflow spec defines two Prometheus metrics with the same `name` but different `help` strings, or a retried/resubmitted workflow changes the help text of a metric name that was already registered in the executor's meter.
Common situations: Copy-pasting a metric block and editing help but not name; retrying a workflow whose updated spec tweaked the help string; templates parameterized such that help text varies per step while name stays constant.
Related errors
- found existing gauge for custom metric %s of type %s
- found existing counter for custom metric %s of type %s
- found existing histogram for custom metric %s of type %s
- found unwanted type %s for custom metric %s of type %s
- failed to create new metric %s
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/cd662f775cecc8a6.
Report an issue: GitHub.