thanos-io/thanos · error

read postings range

Error message

read postings range

What it means

The postings fetch path issues a ranged read (GetRange) against the bucket for a slice of the block's index file. This error wraps any failure of that object-storage ranged GET — network error, object not found, permission or throttling failure — while streaming the postings table portion of index.

Solutions

  1. Retry the query — bucket readers typically retry transient errors; check whether the failure was transient
  2. Verify the block's index file still exists in the bucket and the block has not been deleted (deletion marks)
  3. Check object-store credentials and permissions for reading the index object
  4. Inspect storage provider metrics for throttling (429) and enable request pacing or increase limits
Defensive patterns

Strategy: retry

Validate before calling

exists, err := bkt.Exists(ctx, block.indexFilename())
if err != nil || !exists {
	// block index missing: resync bucket view before querying
}

Type guard

func isRetryableGetRangeErr(err error) bool { return errors.Is(err, context.DeadlineExceeded) || isThrottle(err) || isNetTemporary(err) }

Try / catch

part, err := bkt.GetRange(ctx, name, start, length)
if err != nil {
	if isRetryableGetRangeErr(err) { return retry.WithBackoff(...) }
	return errors.Wrap(err, "read postings range")
}

Prevention

When it happens

Trigger: r.block.bkt.GetRange(ctx, r.block.indexFilename(), start, length) fails during Postings(): object storage unavailable/unreachable, index file deleted from the bucket, credentials lacking read permission, or bucket rate limiting (429/503).

Common situations: S3/GCS/Azure throttling under heavy fan-out queries; temporary network partition between store-gateway and object store; block deleted by compactor while being queried; expired/misconfigured storage credentials.

Related errors


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

Appendix: source

Thrown at pkg/store/bucket.go:3214

		start := int64(part.Start)
		// We assume index does not have any ptrs that has 0 length.
		length := int64(part.End) - start

		// Fetch from object storage concurrently and update stats and posting list.
		g.Go(func() error {
			begin := time.Now()
			stats := new(queryStats)
			defer func() {
				r.stats.merge(stats)
			}()

			brdr := bufioReaderPool.Get().(*bufio.Reader)
			defer bufioReaderPool.Put(brdr)

			partReader, err := r.block.bkt.GetRange(ctx, r.block.indexFilename(), start, length)
			if err != nil {
				return errors.Wrap(err, "read postings range")
			}
			defer runutil.CloseWithLogOnErr(r.logger, partReader, "readIndexRange close range reader")
			brdr.Reset(partReader)

			rdr := newPostingsReaderBuilder(ctx, brdr, ptrs[i:j], start, length)

			stats.postingsFetchCount++
			stats.add(PostingsFetched, j-i, int(length))

			for rdr.Next() {
				diffVarintPostings, postingsCount, keyID := rdr.AtDiffVarint()

				output[keyID] = newDiffVarintPostings(diffVarintPostings, nil)

				startCompression := time.Now()
				dataToCache, err := snappyStreamedEncode(int(postingsCount), diffVarintPostings)
				if err != nil {
					stats.cachedPostingsCompressionErrors += 1

View on GitHub (pinned to 35b8b99117)