thanos-io/thanos · error · ApiError
retrieving tsdb statistics
Error message
retrieving tsdb statistics
What it means
Wraps failures of the status service's TSDBStatistics gRPC call (cardinality analysis) into an ErrorInternal for /api/v1/tsdb/status. The RPC failed or timed out while collecting per-tenant statistics.
Solutions
- Retry with a smaller match[] selector and/or lower limit to reduce scan cost.
- Check store gateway health/logs for OOM or timeouts during TSDB statistics collection.
- Increase request/context timeouts and verify querier-store connectivity.
Defensive patterns
Strategy: retry
Try / catch
const resp = await fetch(`${base}/api/v1/tsdb/status?limit=1000`);
if (!resp.ok) {
const body = await resp.json();
if (body.errorType === 'internal') {
// narrow match[] selector and retry with backoff
return retryWithSmallerSelector();
}
throw new Error(body.error);
} Prevention
- Keep cardinality queries selective with match[] to avoid long scans.
- Set and respect client timeouts shorter than server scan limits.
- Watch store gateway memory during TSDB statistics collection.
When it happens
Trigger: Calling /api/v1/tsdb/status when the underlying status service/store RPC errors: store unreachable, context deadline exceeded during expensive cardinality scans, or store returning an internal error.
Common situations: Large Prometheus TSDBs causing cardinality scans to exceed timeouts; store gateway OOMs or restarts under the head-stats query; network issues between querier and stores.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/23fe22fb526042ff.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/query/v1.go:1762
}
}
if limit > math.MaxInt32 {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("limit %d overflows int32", limit)}, func() {}
}
req := &statuspb.TSDBStatisticsRequest{
Matchers: matchers,
Limit: int32(limit),
PartialResponseStrategy: ps,
}
var stats map[string]*statuspb.TSDBStatisticsEntry
tracing.DoInSpan(ctx, "retrieve_tsdb_statistics", func(ctx context.Context) {
stats, warnings, err = qapi.status.TSDBStatistics(ctx, req)
})
if err != nil {
return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving tsdb statistics")}, func() {}
}
if tenant != "" {
if stats[tenant] == nil {
return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(fmt.Errorf("%s tenant not found", tenant), "retrieving tsdb statistics")}, func() {}
}
return convertToTSDBSTatus(stats[tenant], limit), warnings.AsErrors(), nil, func() {}
}
// Merge statistics from all tenants.
aggregatedStats := &statuspb.TSDBStatisticsEntry{}
for _, v := range stats {
aggregatedStats.Merge(v)
}
return convertToTSDBSTatus(aggregatedStats, limit), warnings.AsErrors(), nil, func() {}
}View on GitHub (pinned to 35b8b99117)