thanos-io/thanos · error
fetching range [ , ]
Error message
fetching range [%d, %d]
What it means
Inside the errgroup in cachedGetRange, each missing subrange is fetched directly from the underlying bucket with Bucket.GetRange. If that call fails, the error is wrapped with "fetching range [start, end]" identifying the failing byte range. This is the parallel-fetch phase that backfills cache misses.
Solutions
- Read the wrapped cause to find the underlying GetRange failure and fix it (network, credentials, objectstore limits).
- Retry; errgroup cancels siblings on first error so a transient blip fails the whole fetch — rerun the query.
- Reduce parallelism / tune GetRange caching config (subrange size, TTL) to lower load on the object store.
- Check gctx cancellation: if the caller canceled, the error is expected and should be ignored/logged at debug level.
Example fix
// before
r, err := cb.GetRange(ctx, name, off, length)
if err != nil {
return errors.Wrapf(err, "fetching range [%d, %d]", off, off+length)
}
// after
r, err := cb.GetRange(ctx, name, off, length)
if err != nil {
if ctx.Err() != nil {
return nil // caller canceled; don't fail on wrapped range fetch
}
return errors.Wrapf(err, "fetching range [%d, %d]", off, off+length)
} Defensive patterns
Strategy: retry
Validate before calling
// Verify object reachability and size before range fan-out
attrs, err := bkt.Attributes(ctx, name)
if err != nil || off+length > attrs.Size {
return errors.New("range out of bounds or object unreachable")
} Try / catch
err := fetchWithRetry(ctx, 3, func() error {
_, err := cb.GetRange(ctx, name, off, length)
return err
})
if err != nil {
if ctx.Err() != nil { return nil } // canceled
return errors.Wrapf(err, "fetching range [%d, %d]", off, off+length)
} Prevention
- Retry transient range-fetch failures; errgroup cancels siblings so rerun the whole fetch
- Respect objectstore rate limits; tune subrange size/parallelism
- Increase LB/proxy timeouts for long range reads
- Distinguish caller cancellation (ctx.Err) from real fetch errors
When it happens
Trigger: CachingBucket.GetRange with cache misses triggers concurrent Bucket.GetRange calls; any underlying read error (objectstore 5xx, timeout, object deleted mid-read, ctx cancellation) is wrapped for the specific missing range.
Common situations: Objectstore throttling/limits when many ranges are fetched in parallel; large index-header or meta files fetched range-wise over flaky networks; query cancellation aborting the errgroup context.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/b059a30dc6f8c704.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/cache/caching_bucket.go:428
missing = append(missing, rng{start: off, end: off + cfg.SubrangeSize})
}
}
missing = mergeRanges(missing, 0) // Merge adjacent ranges.
// Keep merging until we have only max number of ranges (= requests).
for limit := cfg.SubrangeSize; cfg.MaxSubRequests > 0 && len(missing) > cfg.MaxSubRequests; limit = limit * 2 {
missing = mergeRanges(missing, limit)
}
var hitsMutex sync.Mutex
// Run parallel queries for each missing range. Fetched data is stored into 'hits' map, protected by hitsMutex.
g, gctx := errgroup.WithContext(ctx)
for _, m := range missing {
g.Go(func() error {
r, err := cb.Bucket.GetRange(gctx, name, m.start, m.end-m.start)
if err != nil {
return errors.Wrapf(err, "fetching range [%d, %d]", m.start, m.end)
}
defer runutil.CloseWithLogOnErr(cb.logger, r, "fetching range [%d, %d]", m.start, m.end)
var bufSize int64
if lastSubrangeOffset >= m.end {
bufSize = m.end - m.start
} else {
bufSize = ((m.end - m.start) - cfg.SubrangeSize) + int64(lastSubrangeLength)
}
buf := make([]byte, bufSize)
_, err = io.ReadFull(r, buf)
if err != nil {
return errors.Wrapf(err, "fetching range [%d, %d]", m.start, m.end)
}
for off := m.start; off < m.end && gctx.Err() == nil; off += cfg.SubrangeSize {
key := cacheKeys[off]View on GitHub (pinned to 35b8b99117)