{"record":{"id":"5f37f9a97ea9b7e2","repo":"prometheus/prometheus","slug":"invalid-source-label-name-in-label-join-s","errorCode":null,"errorMessage":"invalid source label name in label_join(): %s","messagePattern":"invalid source label name in label_join\\(\\): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"promql/functions.go","lineNumber":2541,"sourceCode":"func funcVector(vectorVals []Vector, _ Matrix, _ parser.Expressions, enh *EvalNodeHelper) (Vector, annotations.Annotations) {\n\treturn append(enh.Out,\n\t\tSample{\n\t\t\tMetric: labels.Labels{},\n\t\t\tF:      vectorVals[0][0].F,\n\t\t}), nil\n}\n\n// label_join function operates only on series; does not look at timestamps or values.\nfunc (ev *evaluator) evalLabelJoin(ctx context.Context, args parser.Expressions) (parser.Value, annotations.Annotations) {\n\tvar (\n\t\tdst       = stringFromArg(args[1])\n\t\tsep       = stringFromArg(args[2])\n\t\tsrcLabels = make([]string, len(args)-3)\n\t)\n\tfor i := 3; i < len(args); i++ {\n\t\tsrc := stringFromArg(args[i])\n\t\tif !model.UTF8Validation.IsValidLabelName(src) {\n\t\t\tpanic(fmt.Errorf(\"invalid source label name in label_join(): %s\", src))\n\t\t}\n\t\tsrcLabels[i-3] = src\n\t}\n\tif !model.UTF8Validation.IsValidLabelName(dst) {\n\t\tpanic(fmt.Errorf(\"invalid destination label name in label_join(): %s\", dst))\n\t}\n\n\tval, ws := ev.eval(ctx, args[0])\n\tmatrix := val.(Matrix)\n\tsrcVals := make([]string, len(srcLabels))\n\tlb := labels.NewBuilder(labels.EmptyLabels())\n\n\tfor i, el := range matrix {\n\t\tfor i, src := range srcLabels {\n\t\t\tsrcVals[i] = el.Metric.Get(src)\n\t\t}\n\t\tstrval := strings.Join(srcVals, sep)\n\t\tlb.Reset(el.Metric)","sourceCodeStart":2523,"sourceCodeEnd":2559,"githubUrl":"https://github.com/prometheus/prometheus/blob/44d6a0e0b1dbdcba4e68853d50e0bafc87d90507/promql/functions.go#L2523-L2559","documentation":"evalLabelJoin validates every source label argument (args[3..]) with IsValidLabelName before evaluating the inner vector; an invalid one panics with 'invalid source label name in label_join(): <src>', converted by the engine into a query error. Note the check validates the NAME of the source label, not its value.","triggerScenarios":"Calling label_join(v, dst, sep, src1, src2, ...) where any src argument is not a valid label name — contains invalid characters/UTF-8, or was mangled by templating (e.g. \"instance \" with a trailing space or a ${var} that rendered empty/malformed).","commonSituations":"Dynamic source-label lists from dashboard variables; refactoring label_replace queries into label_join and reusing a regex fragment as a label name; quoting mistakes where the src becomes '\"job\"' with embedded quotes.","solutions":["Use plain valid label names for every source argument of label_join","If the list is generated, validate each src against a label-name check before building the query","Remove quotes/whitespace artifacts introduced by templating or copy-paste"],"exampleFix":"# before\nlabel_join(up, \"target\", \"/\", \"job\", \"instance \")\n\n# after\nlabel_join(up, \"target\", \"/\", \"job\", \"instance\")","handlingStrategy":"validation","validationCode":"for (const src of srcLabels) {\n  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(src)) throw new Error(`invalid source label: ${src}`);\n}","typeGuard":"const isValidLabelName = (s: string): boolean => /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(s);","tryCatchPattern":null,"preventionTips":["Every label_join source argument must be an existing, valid label name — not a regex","Validate templated source-label lists before building queries"],"tags":["promql","label-join","labels","validation","go"],"backgroundTag":null,"analyzedSha":"44d6a0e0b1dbdcba4e68853d50e0bafc87d90507","analyzedAt":"2026-08-15T08:44:33.427Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}