{"record":{"id":"3c7676717f07ea76","repo":"argoproj/argo-workflows","slug":"invalid-metric-spec","errorCode":null,"errorMessage":"invalid metric spec","messagePattern":"invalid metric spec","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"workflow/metrics/metrics_custom.go","lineNumber":306,"sourceCode":"\t\t}\n\t\treturn nil\n\tcase metricType == wfv1.MetricTypeGauge:\n\t\treturn m.createCustomGauge(metricSpec)\n\tcase metricType == wfv1.MetricTypeHistogram:\n\t\treturn m.CreateInstrument(telemetry.Float64Histogram, metricSpec.Name, metricSpec.Help, \"{item}\", telemetry.WithDefaultBuckets(metricSpec.Histogram.GetBuckets()))\n\tcase metricType == wfv1.MetricTypeCounter:\n\t\terr := m.CreateInstrument(telemetry.Float64ObservableCounter, metricSpec.Name, metricSpec.Help, \"{item}\")\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tinst := m.GetInstrument(metricSpec.Name)\n\t\tif customUserData(inst, false) == nil {\n\t\t\tinst.SetUserdata(newUserData())\n\t\t}\n\t\tcustomInst := customInstrument{Instrument: inst}\n\t\treturn inst.RegisterCallback(m.Metrics, customInst.customCallback)\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid metric spec\")\n\t}\n}\n\nfunc (m *Metrics) createCustomGauge(metricSpec *wfv1.Prometheus) error {\n\terr := m.CreateInstrument(telemetry.Float64ObservableGauge, metricSpec.Name, metricSpec.Help, \"{item}\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tinst := m.GetInstrument(metricSpec.Name)\n\tif customUserData(inst, false) == nil {\n\t\tinst.SetUserdata(newUserData())\n\t}\n\tcustomInst := customInstrument{Instrument: inst}\n\treturn inst.RegisterCallback(m.Metrics, customInst.customCallback)\n}\n\nfunc (m *Metrics) runCustomGC(ttl time.Duration) {\n\tm.IterateROInstruments(func(baseMetric *telemetry.Instrument) {","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/workflow/metrics/metrics_custom.go#L288-L324","documentation":"createCustomMetric dispatches on the metricSpec's metric type; if the type is none of gauge/counter/histogram it returns 'invalid metric spec'. Called from ensureBaseMetric when the instrument does not yet exist, so it fires on the first upsert of a malformed spec.","triggerScenarios":"ensureBaseMetric -> createCustomMetric with a wfv1.Prometheus spec lacking any recognized type stanza (gauge/counter/histogram all nil), typically because the workflow metric entry is empty or the type block failed to parse.","commonSituations":"Empty metrics entries in workflow YAML; wrong indentation placing gauge/counter/histogram outside the metric item; building specs in code without setting a type field.","solutions":["Declare exactly one of gauge:, counter:, or histogram: in the metric spec","Validate the workflow with argo lint before submission to catch missing type blocks","Fix indentation so the type block is nested under the correct metric item"],"exampleFix":"# before\n- name: events\n  counter:\n  value: \"1\"\n# after\n- name: events\n  counter:\n    value: \"1\"","handlingStrategy":"validation","validationCode":"// check the type stanza is nested under the metric item before submit\nfor _, m := range tmpl.Metrics.Prometheus {\n\tif m.Gauge == nil && m.Counter == nil && m.Histogram == nil {\n\t\treturn fmt.Errorf(\"metric %s has no type block\", m.Name)\n\t}\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check YAML indentation: gauge/counter/histogram must be a child of the metric item","Run argo lint in CI","Reject empty metrics entries in templates via review/lint"],"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"}