thanos-io/thanos · error

read range

Error message

read range

What it means

This error wraps any failure that occurs while reading an object-storage byte range into an in-memory buffer inside bucketBlock.readAllChunks/readChunkRange helper. The store gateway fetched a range (e.g. a chunk file segment) via bkt.GetRange and then buffered it with bytes.Buffer.ReadFrom; if the underlying reader (S3/GCS/Azure/etc.) returns any I/O error mid-stream, it is wrapped with the message 'read range'. It is a transport-level failure, not a data-format problem.

Solutions

  1. Inspect the wrapped inner error to identify the object-store client failure and fix root cause (credentials, bucket, endpoint).
  2. Increase object-store client timeout/retry config in the bucket store config (http-client, retries on 5xx).
  3. Verify network path to object storage (VPC endpoints, proxies, DNS) from the store-gateway pods.
  4. Retry the query; Thanos retries bucket requests automatically a few times, transient errors often resolve.

Example fix

// before
buf := bytes.NewBuffer(make([]byte, 0, length+bytes.MinRead))
if _, err := buf.ReadFrom(r); err != nil {
	return nil, errors.Wrap(err, "read range")
}
// after (caller side: bound the request with a context and let retries happen)
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
reader, err := b.bkt.GetRange(ctx, obj, off, length)
if err != nil {
	return nil, errors.Wrap(err, "get range reader")
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure reader/context are bounded before use
if r == nil { return errors.New("nil range reader") }
if _, ok := ctx.Deadline(); !ok {
	var cancel context.CancelFunc
	ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
	defer cancel()
}

Try / catch

if err != nil {
	var netErr net.Error
	if errors.As(err, &netErr) && netErr.Timeout() {
		// retry with backoff
	}
	return errors.Wrap(err, "read range")
}

Prevention

When it happens

Trigger: bucketBlock.readChunkRange/readAllChunks calls buf.ReadFrom(r) on a range reader from bkt.GetRange and the object store read fails mid-stream (connection reset, timeout, 5xx retried exhausted, truncated body).

Common situations: Object storage flakiness (S3 503 slow-down, GCS resets), network partitions between store-gateway and bucket, proxy/LB idle timeouts cutting long chunk downloads, over-aggressive object-store client timeouts.

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/5ab2db5ae46f6029. Report an issue: GitHub.

Appendix: source

Thrown at pkg/store/bucket.go:2522

func (b *bucketBlock) indexFilename() string {
	return path.Join(b.meta.ULID.String(), block.IndexFilename)
}

func (b *bucketBlock) readIndexRange(ctx context.Context, off, length int64, logger log.Logger) ([]byte, error) {
	r, err := b.bkt.GetRange(ctx, b.indexFilename(), off, length)
	if err != nil {
		return nil, errors.Wrap(err, "get range reader")
	}
	defer runutil.CloseWithLogOnErr(logger, r, "readIndexRange close range reader")

	// Preallocate the buffer with the exact size so we don't waste allocations
	// while progressively growing an initial small buffer. The buffer capacity
	// is increased by MinRead to avoid extra allocations due to how ReadFrom()
	// internally works.
	buf := bytes.NewBuffer(make([]byte, 0, length+bytes.MinRead))
	if _, err := buf.ReadFrom(r); err != nil {
		return nil, errors.Wrap(err, "read range")
	}
	return buf.Bytes(), nil
}

func (b *bucketBlock) readChunkRange(ctx context.Context, seq int, off, length int64, chunkRanges byteRanges, logger log.Logger) (*[]byte, error) {
	if seq < 0 || seq >= len(b.chunkObjs) {
		return nil, errors.Errorf("unknown segment file for index %d", seq)
	}

	// Get a reader for the required range.
	reader, err := b.bkt.GetRange(ctx, b.chunkObjs[seq], off, length)
	if err != nil {
		return nil, errors.Wrap(err, "get range reader")
	}
	defer runutil.CloseWithLogOnErr(logger, reader, "readChunkRange close range reader")

	// Get a buffer from the pool.
	chunkBuffer, err := b.chunkPool.Get(chunkRanges.size())

View on GitHub (pinned to 35b8b99117)