{"record":{"id":"15b928b346f86a29","repo":"argoproj/argo-workflows","slug":"found-unwanted-type-s-for-custom-metric-s-of-typ","errorCode":null,"errorMessage":"found unwanted type %s for custom metric %s of type %s","messagePattern":"found unwanted type (.+?) for custom metric (.+?) of type (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/metrics/metrics_custom.go","lineNumber":161,"sourceCode":"\t\tif inst.GetDescription() != metricSpec.Help {\n\t\t\treturn nil, fmt.Errorf(\"help for metric %s is already set to %s, it cannot be changed\", metricSpec.Name, inst.GetDescription())\n\t\t}\n\t\twantedType := metricSpec.GetMetricType()\n\t\tswitch inst.GetOtel().(type) {\n\t\tcase *metric.Float64ObservableGauge:\n\t\t\tif wantedType != wfv1.MetricTypeGauge && !metricSpec.IsRealtime() {\n\t\t\t\treturn nil, fmt.Errorf(\"found existing gauge for custom metric %s of type %s\", metricSpec.Name, wantedType)\n\t\t\t}\n\t\tcase *metric.Float64ObservableCounter:\n\t\t\tif wantedType != wfv1.MetricTypeCounter {\n\t\t\t\treturn nil, fmt.Errorf(\"found existing counter for custom metric %s of type %s\", metricSpec.Name, wantedType)\n\t\t\t}\n\t\tcase *metric.Float64Histogram:\n\t\t\tif wantedType != wfv1.MetricTypeHistogram {\n\t\t\t\treturn nil, fmt.Errorf(\"found existing histogram for custom metric %s of type %s\", metricSpec.Name, wantedType)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"found unwanted type %s for custom metric %s of type %s\", reflect.TypeOf(inst.GetOtel()), metricSpec.Name, wantedType)\n\t\t}\n\t\treturn inst, nil\n\t}\n\treturn nil, nil\n}\n\nfunc (m *Metrics) ensureBaseMetric(metricSpec *wfv1.Prometheus, ownerKey string) (*telemetry.Instrument, error) {\n\t// Fast path: check if metric already exists and is fully initialized (double-checked locking).\n\tmetric, err := m.matchExistingMetric(metricSpec)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif metric != nil {\n\t\tif customUserData(metric, false) != nil {\n\t\t\tm.attachCustomMetricToWorkflow(metricSpec, ownerKey)\n\t\t\treturn metric, nil\n\t\t}\n\t}","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/metrics/metrics_custom.go#L143-L179","documentation":"matchExistingMetric's default branch: an instrument with the metric's name exists in the registry but its underlying OpenTelemetry type is neither Float64ObservableGauge, Float64ObservableCounter, nor Float64Histogram, so the existing-type check cannot be performed and the upsert is rejected. It reports the actual Go type via reflect for diagnosis. In practice this means a base/built-in instrument (or an unexpected wrapper) already owns that name.","triggerScenarios":"UpsertCustomMetric is called with a metricSpec.Name that collides with a registered instrument of an unexpected OTel kind (e.g. a non-observable/base instrument created elsewhere in the controller) — the switch in matchExistingMetric falls through to default and returns this error immediately.","commonSituations":"A custom metric name shadowing a built-in Argo metric name (e.g. same name as a system instrument); upgrades where instrument kinds changed; copying an existing name from another metrics scope.","solutions":["Rename the custom metric to a unique name that does not collide with existing instruments","Check the logged Go type in the error to identify what already registered the name and align usage accordingly","Avoid using names of Argo built-in/base metrics for custom workflow metrics"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// reject custom metric names that shadow Argo built-in metric names\nvar builtinMetrics = map[string]bool{\"operation_count\": true, \"k8s_request_duration\": true}\nfunc isReservedName(name string) bool { return builtinMetrics[name] }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never reuse built-in Argo metric names for custom metrics","Prefix custom metrics with a workflow/team-specific prefix","Read the reflect type in the error to identify what owns the name"],"tags":["metrics","otel","type-conflict","naming-collision"],"backgroundTag":"metric-type-conflict","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}