thanos-io/thanos · error · api.ApiError

Query not explainable

Error message

Query not explainable

What it means

getQueryExplain returns an HTTP 400 (api.ErrorBadData) when the promql.Query object returned by the engine does not implement engine.ExplainableQuery, so Explain() cannot be called. This happens when the query was executed by an engine whose query type lacks explain support (e.g. the plain Prometheus engine instead of the Thanos engine).

Solutions

  1. Pass `engine=thanos` in the request so the query runs on the explainable Thanos engine.
  2. Upgrade Thanos to a version where the Thanos engine supports Explain on all query paths.
  3. Remove explain calls from clients when using the Prometheus engine, since it does not support explain.

Example fix

// before
GET /api/v1/query_explain?query=up&engine=prometheus
// after
GET /api/v1/query_explain?query=up&engine=thanos
Defensive patterns

Strategy: try-catch

Validate before calling

if (engine && engine !== 'thanos') {
  throw new Error('query_explain requires engine=thanos');
}

Try / catch

const body = await res.json();
if (body.status === 'error' && body.error === 'Query not explainable') {
  // fall back to a normal query without explain, or switch engine=thanos
}

Prevention

When it happens

Trigger: Calling the query_explain endpoint (queryExplain/queryRangeExplain) while the effective engine is not the Thanos engine, so the returned query cannot be type-asserted to engine.ExplainableQuery.

Common situations: Older Thanos builds or deployments where the promql engine fallback is active; clients hitting explain endpoints after the engine was switched to 'prometheus'.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

	}

	if len(data) == 0 {
		return nil, nil
	}

	var info storepb.ShardInfo
	if err := json.Unmarshal([]byte(data), &info); err != nil {
		return nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "could not unmarshal parameter %s", ShardInfoParam)}
	}

	return &info, nil
}

func (qapi *QueryAPI) getQueryExplain(query promql.Query) (*engine.ExplainOutputNode, *api.ApiError) {
	if eq, ok := query.(engine.ExplainableQuery); ok {
		return eq.Explain(), nil
	}
	return nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("Query not explainable")}
}

func (qapi *QueryAPI) parseQueryAnalyzeParam(r *http.Request) bool {
	return (r.FormValue(QueryAnalyzeParam) == "true" || r.FormValue(QueryAnalyzeParam) == "1")
}

func analyzeQueryOutput(query promql.Query, engineType PromqlEngineType, tracker *fanout.Tracker) (queryTelemetry, error) {
	if eq, ok := query.(engine.ExplainableQuery); ok {
		if analyze := eq.Analyze(); analyze != nil {
			return processAnalysis(analyze, tracker), nil
		} else {
			return queryTelemetry{}, errors.Errorf("Query: %v not analyzable", query)
		}
	}

	var warning error
	if engineType == PromqlEngineThanos {
		warning = errors.New("Query fallback to prometheus engine; not analyzable.")

View on GitHub (pinned to 35b8b99117)