thanos-io/thanos · error

subrange for offset not found

Error message

subrange for offset %d not found

What it means

subrangeAt looks up a cached subrange by its byte offset in an internal map; if no entry exists for that offset it returns this error. It means the reader's offset-to-key index has no subrange registered at the requested aligned offset, so the requested data cannot be served from cache state.

Solutions

  1. Verify the read offset is within the object size and the reader was created with the correct range
  2. Re-fetch the object range to rebuild the reader's subranges
  3. Check for concurrent eviction of cache entries while the reader is active
  4. Pin the object range in cache or reduce subrangeSize so all needed subranges fit
  5. Update Thanos if you suspect a known subrange-index bug

Example fix

// before
offset := int64(10 << 30) // beyond what was fetched into the reader
r.Read(buf)
// after
if offset >= objectSize { return io.EOF }
reader, err := bucketReader.GetRange(ctx, name, offset, length)
if err != nil { return err }
_, err = reader.Read(buf)
Defensive patterns

Strategy: try-catch

Validate before calling

if offset < 0 || offset+int64(len(buf)) > objectSize { return io.EOF }

Type guard

func offsetInRange(offset int64, size int64) bool { return offset >= 0 && offset < size }

Try / catch

n, err := reader.Read(buf)
if err != nil {
    if strings.Contains(err.Error(), "subrange for offset") {
        // rebuild reader via a fresh GetRange call
        reader, err = bucket.GetRange(ctx, name, off, len)
    }
}

Prevention

When it happens

Trigger: Read computes currentSubrangeOffset := (c.readOffset / c.subrangeSize) * c.subrangeSize and calls subrangeAt, but c.subranges has no entry for that offset key — e.g. the offset is beyond populated subranges or the map was built incompletely.

Common situations: Reading past the populated range of a cached object, cache entries deleted between index build and read, or reusing a reader after its underlying subranges were rebuilt/evicted.

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/63843adcb6eab0b5. Report an issue: GitHub.

Appendix: source

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

	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)
	c.remaining -= int64(toCopy)

	return toCopy, nil
}

func (c *subrangesReader) subrangeAt(offset int64) ([]byte, error) {
	b := c.subranges[c.offsetsKeys[offset]]
	if b == nil {
		return nil, errors.Errorf("subrange for offset %d not found", offset)
	}
	return b, nil
}

type getReader struct {
	c         cache.Cache
	ctx       context.Context
	r         io.ReadCloser
	buf       *bytes.Buffer
	startTime time.Time
	ttl       time.Duration
	cacheKey  string
	maxSize   int
}

func (g *getReader) Close() error {
	// We don't know if entire object was read, don't store it here.
	g.buf = nil

View on GitHub (pinned to 35b8b99117)