thanos-io/thanos · error

reading postings

Error message

reading postings

What it means

After streaming the postings range through the postings reader (newPostingsReaderBuilder over a bufio.Reader), rdr.Error() reports any error accumulated while parsing the postings stream. This error means the ranged bytes from the index were read (or the underlying object-storage stream errored) but could not be fully parsed into postings.

Solutions

  1. Retry the query; transient stream errors are the most common cause
  2. Check query/store-gateway logs for context cancellation and increase query timeouts if applicable
  3. Validate the block (thanos tools bucket verify) — re-upload or delete the block if the index is corrupted
  4. Ensure the block is not modified/deleted while readers are using it (use deletion marks and proper block lifecycle)
Defensive patterns

Strategy: retry

Validate before calling

select {
case <-ctx.Done():
	// abort before streaming: context already canceled
	return ctx.Err()
default:
}

Type guard

func isStreamErr(err error) bool { return errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) }

Try / catch

if err := rdr.Error(); err != nil {
	if isStreamErr(err) {
		return retry.WithBackoff(ctx, func() error { return fetchPostingsRange(ctx, ptrs) })
	}
	return errors.Wrap(err, "reading postings")
}

Prevention

When it happens

Trigger: The object-storage range reader returned an error mid-stream (connection reset, context cancellation, timeout), or the postings binary data in the index range is corrupt/truncated so the diff-varint decoding fails while rdr iterates over ptrs[i:j].

Common situations: Unstable connection to S3/GCS causing mid-body stream failures; context canceled by client query timeout/cancellation; corrupted or partially uploaded index block; block replaced in the bucket while being read.

Related errors


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

Appendix: source

Thrown at pkg/store/bucket.go:3248

				dataToCache, err := snappyStreamedEncode(int(postingsCount), diffVarintPostings)
				if err != nil {
					stats.cachedPostingsCompressionErrors += 1
					return errors.Wrap(err, "encoding with snappy")
				}

				stats.cachedPostingsCompressions += 1
				stats.CachedPostingsOriginalSizeSum += units.Base2Bytes(len(diffVarintPostings))
				stats.CachedPostingsCompressedSizeSum += units.Base2Bytes(len(dataToCache))
				stats.CachedPostingsCompressionTimeSum += time.Since(startCompression)
				stats.add(PostingsTouched, 1, len(diffVarintPostings))

				r.block.indexCache.StorePostings(r.block.meta.ULID, keys[keyID], dataToCache, tenant)
			}

			stats.PostingsFetchDurationSum += time.Since(begin)

			if err := rdr.Error(); err != nil {
				return errors.Wrap(err, "reading postings")
			}
			return nil
		})
	}

	return output, closeFns, g.Wait()
}

func (r *bucketIndexReader) decodeCachedPostings(b []byte) (index.Postings, []func(), error) {
	// Even if this instance is not using compression, there may be compressed
	// entries in the cache written by other stores.
	var (
		l        index.Postings
		err      error
		closeFns []func()
	)
	if isDiffVarintSnappyEncodedPostings(b) || isDiffVarintSnappyStreamedEncodedPostings(b) {
		s := time.Now()

View on GitHub (pinned to 35b8b99117)