thanos-io/thanos · error · ApiError
no match[] parameter provided
Error message
no match[] parameter provided
What it means
The series endpoint requires at least one match[] parameter to select series; if r.Form[MatcherParam] is empty it returns ErrorBadData (HTTP 400) "no match[] parameter provided". Without matchers the querier cannot determine which series to return.
Solutions
- Add at least one match[] parameter, e.g. ?match[]=up
- If a dashboard variable may be empty, substitute a catch-all matcher like {__name__!=""}
- Verify URL encoding so match[] actually reaches the server
Example fix
// before GET /api/v1/series // after GET /api/v1/series?match[]=up
Defensive patterns
Strategy: validation
Validate before calling
if len(matchers) == 0 {
matchers = []string{`{__name__!=""}`} // or return an error to the user first
} Prevention
- Treat match[] as mandatory for /api/v1/series and validate before sending
- Guard dashboard template variables with fallback matchers
- Log the fully-built URL in clients to catch silently dropped parameters
When it happens
Trigger: GET/POST /api/v1/series without any match[] form value, or with match[] present but an empty string so it doesn't register in r.Form.
Common situations: Clients forgetting the mandatory parameter; URL encoding issues dropping the value; dashboards with unset template variables producing empty match[].
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/05c7b858cc7ad800.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/query/v1.go:1176
if vals == nil {
vals = make([]string, 0)
}
if limit > 0 && len(vals) > limit {
vals = vals[:limit]
warnings = warnings.Add(errors.New("results truncated due to limit"))
}
return vals, warnings.AsErrors(), nil, func() {}
}
func (qapi *QueryAPI) series(r *http.Request) (any, []error, *api.ApiError, func()) {
if err := r.ParseForm(); err != nil {
return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}, func() {}
}
if len(r.Form[MatcherParam]) == 0 {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("no match[] parameter provided")}, func() {}
}
start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
if err != nil {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
}
matcherSets, ctx, err := tenancy.RewriteLabelMatchers(r.Context(), r, qapi.tenantHeader, qapi.defaultTenant, qapi.tenantCertField, qapi.enforceTenancy, qapi.tenantLabel, r.Form[MatcherParam])
if err != nil {
apiErr := &api.ApiError{Typ: api.ErrorBadData, Err: err}
return nil, nil, apiErr, func() {}
}
enableDedup, apiErr := qapi.parseEnableDedupParam(r)
if apiErr != nil {
return nil, nil, apiErr, func() {}
}
View on GitHub (pinned to 35b8b99117)