{"record":{"id":"b9613531bd893660","repo":"argoproj/argo-workflows","slug":"invalid-custom-metric-type","errorCode":null,"errorMessage":"invalid custom metric type","messagePattern":"invalid custom metric type","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/metrics/metrics_custom.go","lineNumber":258,"sourceCode":"\t\t\tfallthrough\n\t\tdefault:\n\t\t\tmetricValue.prometheusValue = val\n\t\t}\n\tcase metricType == wfv1.MetricTypeHistogram:\n\t\tval, err := strconv.ParseFloat(metricSpec.Histogram.Value, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t// Record does its own locking if needed by the exporter\n\t\tbaseMetric.Record(ctx, val, metricValue.getLabels())\n\tcase metricType == wfv1.MetricTypeCounter:\n\t\tval, err := strconv.ParseFloat(metricSpec.Counter.Value, 64)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tmetricValue.prometheusValue += val\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid custom metric type\")\n\t}\n\treturn nil\n}\n\nfunc (m *Metrics) attachCustomMetricToWorkflow(metricSpec *wfv1.Prometheus, ownerKey string) {\n\tif !metricSpec.IsRealtime() {\n\t\treturn\n\t}\n\tm.realtimeMutex.Lock()\n\tdefer m.realtimeMutex.Unlock()\n\tmetricKey := metricSpec.GetKey()\n\tfor _, tracker := range m.realtimeWorkflows[ownerKey] {\n\t\tif tracker.key == metricKey {\n\t\t\treturn\n\t\t}\n\t}\n\tm.realtimeWorkflows[ownerKey] = append(m.realtimeWorkflows[ownerKey], realtimeTracker{\n\t\tinst: m.GetInstrument(metricSpec.Name),","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/metrics/metrics_custom.go#L240-L276","documentation":"UpsertCustomMetric switches on the metricSpec shape (Gauge/Counter/Histogram/realtime); if the spec has none of the recognized metric kinds set — its GetMetricType() is unsupported — the default branch returns 'invalid custom metric type'. The Prometheus spec in the workflow must declare exactly one of gauge, counter, or histogram.","triggerScenarios":"Submitting/upserting a metric whose wfv1.Prometheus spec has no gauge/counter/histogram stanza (all nil), or an empty/zero-value Prometheus entry in the workflow's metrics list.","commonSituations":"Hand-edited workflow YAML where the type block was deleted or mis-indented; programmatically built specs leaving the type field unset; typos in the YAML key (e.g. 'guage') that silently parse as an unknown field.","solutions":["Add exactly one metric type block (gauge:, counter:, or histogram:) to the metric spec","Fix YAML key typos so the intended type block actually parses (argo lint helps)","Ensure the spec is constructed with the correct field when building Prometheus objects in code"],"exampleFix":"# before\nmetrics:\n  - name: my_metric\n    value: \"1\"\n# after\nmetrics:\n  - name: my_metric\n    gauge:\n      value: \"1\"","handlingStrategy":"validation","validationCode":"// ensure the spec declares exactly one metric type\nfunc hasMetricType(m *wfv1.Prometheus) bool {\n\treturn m.GetMetricType() != \"\" && (m.Gauge != nil || m.Counter != nil || m.Histogram != nil)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include one of gauge:/counter:/histogram: per metric entry","Run argo lint before submit","Watch for YAML key typos ('guage') that silently drop the type block","When building specs in code, set the type field explicitly and unit-test it"],"tags":["metrics","validation","workflow-spec"],"backgroundTag":"invalid-metric-spec","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"}