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

  1. 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.
  2. Quote the YAML value: use matchers: ['foo{bar="baz"}'] so YAML does not strip or reinterpret braces and quotes.
  3. Ensure the string is a bare instant-vector selector (no range [5m], no offset, no functions/operators).
  4. Check label and metric names against Prometheus naming rules ([a-zA-Z_:][a-zA-Z0-9_:]* for names).
  5. 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

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


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)