thanos-io/thanos · error

read range for seq offset

Error message

read range for seq %d offset %x

What it means

Raised in bucketChunkReader.load when io.ReadFull fails to read the pre-computed byte range for chunk (seq, offset) from the object storage stream. Any error other than an expected ErrUnexpectedEOF on the last chunk is fatal — the fetched range is incomplete or the storage stream broke.

Solutions

  1. Retry the query to rule out transient object store errors
  2. Verify range GET support and integrity of the chunks/index files in the bucket
  3. Increase the fetch window if chunks routinely exceed estimatedMaxChunkSize
Defensive patterns

Strategy: retry

When it happens

Trigger: Thrown at pkg/store/bucket.go:3785 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at pkg/store/bucket.go:3785

				return errors.Wrap(err, "fast forward range reader")
			}
			readOffset += written
		}
		// Presume chunk length to be reasonably large for common use cases.
		// However, declaration for EstimatedMaxChunkSize warns us some chunks could be larger in some rare cases.
		// This is handled further down below.
		chunkLen = r.block.estimatedMaxChunkSize
		if i+1 < len(pIdxs) {
			if diff = pIdxs[i+1].offset - pIdx.offset; int(diff) < chunkLen {
				chunkLen = int(diff)
			}
		}
		cb := buf[:chunkLen]
		n, err = io.ReadFull(bufReader, cb)
		readOffset += n
		// Unexpected EOF for last chunk could be a valid case. Any other errors are definitely real.
		if err != nil && !errors.Is(err, io.ErrUnexpectedEOF) && i != len(pIdxs)-1 {
			return errors.Wrapf(err, "read range for seq %d offset %x", seq, pIdx.offset)
		}

		chunkDataLen, n := binary.Uvarint(cb)
		if n < 1 {
			return errors.New("reading chunk length failed")
		}

		// Chunk length is n (number of bytes used to encode chunk data), 1 for chunk encoding and chunkDataLen for actual chunk data.
		// There is also crc32 after the chunk, but we ignore that.
		chunkLen = n + 1 + int(chunkDataLen)
		if chunkLen <= len(cb) {
			c := rawChunk(cb[n:chunkLen])
			err = populateChunk(&(res[pIdx.seriesEntry].chks[pIdx.chunk]), &c, aggrs, r.save, calculateChunkChecksum)
			if err != nil {
				return errors.Wrap(err, "populate chunk")
			}
			stats.add(ChunksTouched, 1, int(chunkDataLen))
			continue

View on GitHub (pinned to 35b8b99117)