{"record":{"id":"1b5c068e6146ae21","repo":"thanos-io/thanos","slug":"parser-parsemetricselector","errorCode":null,"errorMessage":"parser ParseMetricSelector","messagePattern":"parser ParseMetricSelector","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/rules/rules.go","lineNumber":69,"sourceCode":"\treturn c\n}\n\nfunc (rr *GRPCClient) Rules(ctx context.Context, req *rulespb.RulesRequest) (*rulespb.RuleGroups, annotations.Annotations, error) {\n\tspan, ctx := tracing.StartSpan(ctx, \"rules_request\")\n\tdefer span.Finish()\n\n\tresp := &rulesServer{ctx: ctx}\n\n\tif err := rr.proxy.Rules(req, resp); err != nil {\n\t\treturn nil, nil, errors.Wrap(err, \"proxy Rules\")\n\t}\n\n\tvar err error\n\tmatcherSets := make([][]*labels.Matcher, len(req.MatcherString))\n\tfor i, s := range req.MatcherString {\n\t\tmatcherSets[i], err = extpromql.ParseMetricSelector(s)\n\t\tif err != nil {\n\t\t\treturn nil, nil, errors.Wrap(err, \"parser ParseMetricSelector\")\n\t\t}\n\t}\n\n\tresp.groups = filterRulesByMatchers(resp.groups, matcherSets)\n\tresp.groups = filterRulesByNamesAndFile(resp.groups, req.RuleName, req.RuleGroup, req.File)\n\n\t// TODO(bwplotka): Move to SortInterface with equal method and heap.\n\tresp.groups = dedupGroups(resp.groups)\n\tfor _, g := range resp.groups {\n\t\tg.Rules = dedupRules(g.Rules, rr.replicaLabels)\n\t}\n\n\treturn &rulespb.RuleGroups{Groups: resp.groups}, resp.warnings, nil\n}\n\n// filters rules by group name, rule name or file.\nfunc filterRulesByNamesAndFile(ruleGroups []*rulespb.RuleGroup, ruleName []string, ruleGroup []string, file []string) []*rulespb.RuleGroup {\n\tif len(ruleName) == 0 && len(ruleGroup) == 0 && len(file) == 0 {","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/rules/rules.go#L51-L87","documentation":"After proxying, the handler parses every MatcherString in the request as a PromQL metric selector via extpromql.ParseMetricSelector; a parse failure is wrapped as 'parser ParseMetricSelector'. It means one of the requested matcher strings is not a valid metric selector like {job=\"foo\",env=~\"prod.*\"}.","triggerScenarios":"Calling the Rules API with req.MatcherString entries that are empty strings or not parseable PromQL selectors (unbalanced braces, invalid label matchers, bad regex).","commonSituations":"Hand-built matcher strings from user input; shell quoting stripping braces or quotes; passing label names or LogQL instead of PromQL selectors; upgrading promql parser versions that reject previously tolerated syntax.","solutions":["Validate each matcher string with promql.ParseMetricSelector client-side before calling the API","Fix malformed selectors (balanced braces, quoted values, valid matchers =,!=,=~,!~)","Use the SDK/helper to construct matcher sets programmatically instead of string concatenation"],"exampleFix":"// before\nreq.MatcherString = []string{\"{job=prod}\"} // invalid: unquoted value\n// after\nreq.MatcherString = []string{\"{job=\\\"prod\\\"}\"}","handlingStrategy":"validation","validationCode":"for _, s := range matchers {\n    if _, err := promql.ParseMetricSelector(s); err != nil {\n        return fmt.Errorf(\"invalid matcher %q: %w\", s, err)\n    }\n}","typeGuard":"func isValidMetricSelector(s string) bool {\n    _, err := extpromql.ParseMetricSelector(s)\n    return err == nil\n}","tryCatchPattern":null,"preventionTips":["Build matchers programmatically via labels.ParseMatcher instead of string concat","Quote braces and values carefully when passing selectors through shells","Test matcher strings with a parser before sending"],"tags":["promql","validation","parsing"],"backgroundTag":"invalid-query-parameter","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}