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
- URL-encode special characters in replica_labels values (use --data-urlencode with curl)
- Send GET requests with a properly encoded query string, or POST with Content-Type: application/x-www-form-urlencoded
- Do not send JSON or multipart bodies to these endpoints
- 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
- Always percent-encode form values (encodeURIComponent / --data-urlencode)
- Use application/x-www-form-urlencoded, never JSON bodies
- Round-trip test encode->decode before sending
- Watch proxies that rewrite or truncate bodies
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
- ULID is not valid
- not supported marker
- ' ' parameter
- ' ' bad engine
- negative ' ' is not accepted. Try a positive integer
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)