thanos-io/thanos · error

unmarshal query range response

Error message

unmarshal query range response

What it means

QueryRange fetches /api/v1/query_range from a Prometheus/Thanos endpoint and decodes the 2xx response body into the API envelope struct (data.resultType, data.result, error, errorType, warnings). This error is wrapped around json.Unmarshal when the body is not valid JSON or its shape does not match the envelope (e.g. an HTML error page, truncated body, or a proxy response). It means the HTTP layer succeeded (status 2xx) but the payload could not be parsed as a Prometheus query_range response.

Solutions

  1. Verify the URL points to the Prometheus/Thanos API root (e.g. https://thanos-query:9090) and not a UI or wrong path; log/print the raw response body to see what is actually returned.
  2. Check intermediary proxies, gateways, or sidecars (ingress, envoy) that may replace the body with an HTML error page while keeping status 200.
  3. Confirm content-encoding handling: if the server gzips the response, ensure the HTTP client transport decompresses it (DisableCompression not set incorrectly).
  4. Reproduce with curl -v '<base>/api/v1/query_range?query=up&start=...&end=...&step=60' and inspect the body and headers.

Example fix

// before: opaque failure
curl https://wrong-host/graph
// after: correct API base URL
https://thanos-query:9090  // /api/v1/query_range is appended by the client
Defensive patterns

Strategy: try-catch

Try / catch

matrix, _, _, err := client.QueryRange(ctx, u, q, st, en, step, opts)
if err != nil {
    if strings.Contains(err.Error(), "unmarshal query range response") {
        // 2xx but non-JSON body: log body/URL, suspect proxy or wrong endpoint
    }
    return err
}

Prevention

When it happens

Trigger: Any call to Client.QueryRange (via Exec) where the server returns 2xx with a body that fails json.Unmarshal against the envelope struct: non-JSON body, invalid UTF-8, JSON with wrong types (e.g. data as array instead of object), or truncated/garbled response from an intermediary.

Common situations: Pointing Thanos Query at a non-Prometheus endpoint (UI route returning HTML behind a 200), a reverse proxy or service mesh returning an HTML/empty 200 page, TLS-terminating gateway injecting content, or compression/middleware mangling the body.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at pkg/promclient/promclient.go:580

	}

	// Decode only ResultType and load Result only as RawJson since we don't know
	// structure of the Result yet.
	var m struct {
		Data struct {
			ResultType  string          `json:"resultType"`
			Result      json.RawMessage `json:"result"`
			Explanation *Explanation    `json:"explanation,omitempty"`
		} `json:"data"`

		Error     string `json:"error,omitempty"`
		ErrorType string `json:"errorType,omitempty"`
		// Extra fields supported by Thanos Querier.
		Warnings []string `json:"warnings"`
	}

	if err = json.Unmarshal(body, &m); err != nil {
		return nil, nil, nil, errors.Wrap(err, "unmarshal query range response")
	}

	var matrixResult model.Matrix

	// Decode the Result depending on the ResultType
	switch m.Data.ResultType {
	case string(parser.ValueTypeMatrix):
		if err = json.Unmarshal(m.Data.Result, &matrixResult); err != nil {
			return nil, nil, nil, errors.Wrap(err, "decode result into ValueTypeMatrix")
		}
	default:
		if m.Warnings != nil {
			return nil, nil, nil, errors.Errorf("error: %s, type: %s, warning: %s", m.Error, m.ErrorType, strings.Join(m.Warnings, ", "))
		}
		if m.Error != "" {
			return nil, nil, nil, errors.Errorf("error: %s, type: %s", m.Error, m.ErrorType)
		}

View on GitHub (pinned to 35b8b99117)