thanos-io/thanos · error
parse metric selector
Error message
parse metric selector %v
What it means
Matchers.UnmarshalYAML parses a YAML node's string value as a Prometheus metric selector via extpromql.ParseMetricSelector. This error wraps the PromQL parser failure when the selector text is not a valid instant-vector selector, e.g. missing braces, bad label matcher syntax, or invalid label names/values. It is thrown while decoding M3 namespace deletion requests or other YAML config that embeds Matchers fields.
Solutions
- Validate the selector string in promtool or a Prometheus query first (e.g. 'promtool check ...' or test in the Prometheus UI) and fix syntax errors — unbalanced braces/quotes are the most common cause.
- Quote the YAML value: use matchers: ['foo{bar="baz"}'] so YAML does not strip or reinterpret braces and quotes.
- Ensure the string is a bare instant-vector selector (no range [5m], no offset, no functions/operators).
- Check label and metric names against Prometheus naming rules ([a-zA-Z_:][a-zA-Z0-9_:]* for names).
- Use the JSON API field instead of YAML if quoting is error-prone: {"matchers": [{"type":"="","name":"bar","value":"baz"}]}.
Example fix
// before: YAML unquotes value, parser sees foo{bar="baz" without quotes
matchers:
- foo{bar="baz"}
// after: explicitly quoted single-line selector
matchers:
- 'foo{bar="baz"}' Defensive patterns
Strategy: validation
Validate before calling
import "github.com/m3db/m3/src/query/util/exec"
// or validate with promql parser before submitting:
if _, err := extpromql.ParseMetricSelector(selector); err != nil {
return fmt.Errorf("invalid selector %q: %w", selector, err)
} Type guard
func isValidSelector(s string) bool {
_, err := extpromql.ParseMetricSelector(s)
return err == nil
} Try / catch
var m metadata.Matchers
if err := yaml.Unmarshal(cfg, &m); err != nil {
if strings.Contains(err.Error(), "parse metric selector") {
return fmt.Errorf("bad matchers in config: %w", err)
}
return err
} Prevention
- Quote selector strings in YAML with single quotes so braces/quotes survive YAML parsing.
- Validate selectors with promtool or the Prometheus UI before putting them in config.
- Use only instant-vector selectors — no ranges, offsets, or functions — in Matchers fields.
- Prefer the JSON API matcher objects (type/name/value) when generating requests programmatically.
When it happens
Trigger: Submitting a DeletionRequest (or any YAML config with a matchers field) where a matcher string like 'foo{bar="baz"' or 'foo=' fails to parse as a PromQL selector; unquoted YAML values containing characters YAML mangles (e.g. braces or quotes); empty selector strings.
Common situations: Operators writing m3ctl deletion requests by hand and misplacing quotes/braces; YAML interpreting special characters (", {, :) so the value seen by the parser differs from intent; using metric names or label names that violate Prometheus naming rules; copying selectors from PromQL range queries (with [5m]) into a field that only accepts instant selectors.
Related errors
- parse limit configuration
- unable to unmarshal config content
- error while parsing config for request logging
- error while parsing tsdb selector configuration
- --receive.lazy-retrieval-max-buffered-responses must be > 0
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/7d1b80857034cb24.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/metadata/meta.go:152
}
return v, nil
}
type Rewrite struct {
// ULIDs of all source head blocks that went into the block.
Sources []ulid.ULID `json:"sources,omitempty"`
// Deletions if applied (in order).
DeletionsApplied []DeletionRequest `json:"deletions_applied,omitempty"`
// Relabels if applied.
RelabelsApplied []*relabel.Config `json:"relabels_applied,omitempty"`
}
type Matchers []*labels.Matcher
func (m *Matchers) UnmarshalYAML(value *yaml.Node) (err error) {
*m, err = extpromql.ParseMetricSelector(value.Value)
if err != nil {
return errors.Wrapf(err, "parse metric selector %v", value.Value)
}
return nil
}
type DeletionRequest struct {
Matchers Matchers `json:"matchers" yaml:"matchers"`
Intervals tombstones.Intervals `json:"intervals,omitempty" yaml:"intervals,omitempty"`
RequestID string `json:"request_id,omitempty" yaml:"request_id,omitempty"`
}
type File struct {
RelPath string `json:"rel_path"`
// SizeBytes is optional (e.g meta.json does not show size).
SizeBytes int64 `json:"size_bytes,omitempty"`
// Hash is an optional hash of this file. Used for potentially avoiding an extra download.
Hash *ObjectHash `json:"hash,omitempty"`
}View on GitHub (pinned to 35b8b99117)