thanos-io/thanos · error · api.ApiError

parse form

Error message

parse form

What it means

parseReplicaLabelsParam calls r.ParseForm() to read replica_labels from the request body/query; if form parsing fails (e.g. malformed body or invalid URL encoding), an ErrorInternal wrapped as "parse form" is returned. Unlike the other parse errors this is an internal/class 500-style error, not client bad data.

Solutions

  1. URL-encode special characters in replica_labels values (use --data-urlencode with curl)
  2. Send GET requests with a properly encoded query string, or POST with Content-Type: application/x-www-form-urlencoded
  3. Do not send JSON or multipart bodies to these endpoints
  4. Inspect/proxy logs for request-body truncation or encoding mangling

Example fix

// before
curl -X POST host/api/v1/query -d 'replica_labels=cluster&replica_labels=50%'
// after
curl -X POST host/api/v1/query --data-urlencode 'replica_labels=cluster' --data-urlencode 'replica_labels=50%'
Defensive patterns

Strategy: try-catch

Validate before calling

function encodeFormValue(v) { return encodeURIComponent(String(v)); }
const body = pairs.map(([k,v]) => `${k}=${encodeFormValue(v)}`).join('&');
try { decodeURIComponent(body); } catch { throw new Error('malformed encoding in form body'); }

Type guard

null

Try / catch

try {
  const res = await fetch('/api/v1/query', {method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body});
  const j = await res.json();
  if (j.status === 'error' && /parse form/.test(j.error)) throw new Error(j.error);
} catch (e) {
  if (/parse form/.test(e.message)) { /* re-encode request properly */ }
  throw e;
}

Prevention

When it happens

Trigger: Sending a POST to query/query_range/series with a Content-Type or body that cannot be parsed as a form (e.g. corrupt URL encoding like %ZZ, wrong Content-Type with garbage body, oversized/aborted body).

Common situations: HTTP clients sending JSON bodies to form-expecting endpoints; double-encoding or truncating query strings through proxies; curl commands with unescaped special characters (&, %) in replica_labels values.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/9e4cf7dd0f850b78. Report an issue: GitHub.

Appendix: source

Thrown at pkg/api/query/v1.go:326

}

func (qapi *QueryAPI) parseEngineParam(r *http.Request) (e PromqlEngineType, _ *api.ApiError) {
	param := PromqlEngineType(r.FormValue(EngineParam))
	if param == "" {
		param = qapi.defaultEngine
	}
	switch param {
	case PromqlEnginePrometheus, PromqlEngineThanos:
	default:
		return param, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("'%s' bad engine", param)}
	}

	return param, nil
}

func (qapi *QueryAPI) parseReplicaLabelsParam(r *http.Request) (replicaLabels []string, _ *api.ApiError) {
	if err := r.ParseForm(); err != nil {
		return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
	}

	replicaLabels = qapi.replicaLabels
	// Overwrite the cli flag when provided as a query parameter.
	if len(r.Form[ReplicaLabelsParam]) > 0 {
		replicaLabels = r.Form[ReplicaLabelsParam]
	}
	return replicaLabels, nil
}

func (qapi *QueryAPI) parseStoreDebugMatchersParam(r *http.Request) (storeMatchers [][]*labels.Matcher, _ *api.ApiError) {
	if err := r.ParseForm(); err != nil {
		return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
	}

	for _, s := range r.Form[StoreMatcherParam] {
		matchers, err := extpromql.ParseMetricSelector(s)
		if err != nil {

View on GitHub (pinned to 35b8b99117)