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

  1. Read the wrapped cause to find the underlying GetRange failure and fix it (network, credentials, objectstore limits).
  2. Retry; errgroup cancels siblings on first error so a transient blip fails the whole fetch — rerun the query.
  3. Reduce parallelism / tune GetRange caching config (subrange size, TTL) to lower load on the object store.
  4. 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

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)