thanos-io/thanos · error

read position

Error message

read position: %d

What it means

This error wraps any failure from subrangeAt while reading cached object data. It reports the reader's current logical read offset (c.readOffset) so the developer can tell which position in the cached object could not be resolved to a subrange. It is thrown by subrangesReader.Read in Thanos's caching bucket layer when the internal subrange index is inconsistent.

Solutions

  1. Check that the reader is not shared between goroutines without synchronization
  2. Verify the cache backend is healthy and entries were not evicted/truncated mid-read
  3. Log c.readOffset, c.subrangeSize and available subrange keys to diagnose the mismatch
  4. Update Thanos to the latest patch release; known subrange bugs have been fixed
  5. As a workaround, disable the caching bucket for this reader path (use the plain bucket)

Example fix

// before
n, err := r.Read(buf) // panics/errors deep in subrangeAt
// after
if r, ok := r.(*storecache.SubrangesReader); ok {
    // use a fresh reader per request and limit reads to advertised size
}
n, err := io.ReadFull(freshReader, buf[:min(len(buf), size)])
Defensive patterns

Strategy: try-catch

Validate before calling

if reader == nil || objectSize <= 0 { return errors.New("invalid reader or size") }

Type guard

func usableReader(r io.Reader, size int64) bool { return r != nil && size > 0 }

Try / catch

n, err := reader.Read(buf)
if err != nil {
    var wrapped interface{ Cause() error }
    if errors.As(err, &cause) && strings.Contains(err.Error(), "read position:") {
        // fall back to a fresh GetRange for the failed offset
    }
    return err
}

Prevention

When it happens

Trigger: Calling Read on a reader obtained from a caching bucket when c.subranges has no entry for the subrange-aligned offset derived from c.readOffset (c.offsetsKeys lacks the computed key), e.g. after internal state corruption or a partially populated subrange map.

Common situations: Seen when debugging Thanos caching-bucket issues: cache entries evicted or truncated inconsistently, concurrent use of a single subrangesReader across goroutines, or version incompatibilities in cached data layout.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at pkg/store/cache/caching_bucket.go:536

	return &subrangesReader{
		subrangeSize: subrangeSize,
		offsetsKeys:  offsetsKeys,
		subranges:    subranges,

		readOffset: readOffset,
		remaining:  remaining,
	}
}

func (c *subrangesReader) Read(p []byte) (n int, err error) {
	if c.remaining <= 0 {
		return 0, io.EOF
	}

	currentSubrangeOffset := (c.readOffset / c.subrangeSize) * c.subrangeSize
	currentSubrange, err := c.subrangeAt(currentSubrangeOffset)
	if err != nil {
		return 0, errors.Wrapf(err, "read position: %d", c.readOffset)
	}

	offsetInSubrange := int(c.readOffset - currentSubrangeOffset)
	toCopy := len(currentSubrange) - offsetInSubrange
	if toCopy <= 0 {
		// This can only happen if subrange's length is not subrangeSize, and reader is told to read more data.
		return 0, errors.Errorf("no more data left in subrange at position %d, subrange length %d, reading position %d", currentSubrangeOffset, len(currentSubrange), c.readOffset)
	}

	if len(p) < toCopy {
		toCopy = len(p)
	}
	if c.remaining < int64(toCopy) {
		toCopy = int(c.remaining) // Conversion is safe, c.remaining is small enough.
	}

	copy(p, currentSubrange[offsetInSubrange:offsetInSubrange+toCopy])
	c.readOffset += int64(toCopy)

View on GitHub (pinned to 35b8b99117)