thanos-io/thanos · error
reading series length failed
Error message
reading series length failed
What it means
While decoding a fetched index byte range, each series entry begins with a uvarint length. binary.Uvarint returning n < 1 means the bytes at the expected offset do not even contain a valid length prefix, so the reader cannot parse the series record. This indicates the fetched range does not align with actual series data — i.e. corrupt, truncated, or stale index bytes.
Solutions
- Clear/invalidate the series cache for the affected block (or flush the whole index cache) and retry
- Verify the block's index integrity (thanos tools bucket verify); delete/re-upload the block if corrupt
- Ensure series refs come from the same block's postings — a ref from another block produces bogus offsets
- Check that block ULIDs are unique (rebuilt blocks must not reuse ULIDs) to avoid cache key collisions
Defensive patterns
Strategy: validation
Validate before calling
if uint64(id) < start || uint64(id)-start >= uint64(len(b)) {
return errors.Errorf("series id %d outside fetched range [%d,%d)", id, start, len(b))
} Type guard
func offsetInRange(b []byte, id, start uint64) bool { return id >= start && id-start < uint64(len(b)) } Try / catch
l, n := binary.Uvarint(c)
if n < 1 {
logger.Warn("invalid series record; invalidating cache entry", "id", id)
seriesCache.Invalidate(blockMeta.ULID, id)
return retryFetchSeries(ctx, ids) // refetch without cache
} Prevention
- Flush series cache when rebuilding blocks (never reuse ULIDs)
- Validate blocks with bucket verify before serving
- Ensure refs are looked up against the same block
- Limit cache backends to binary-safe value handling
When it happens
Trigger: DecodeSeries parses b[uint64(id)-start:] and binary.Uvarint fails (n < 1): id offset outside the fetched range, truncated read from object storage/cache, or index bytes corrupted in cache.
Common situations: Corrupted entries in the external series cache (e.g. Redis truncation); a series ref (id) that does not belong to the fetched block/range; partially uploaded or corrupted index file in the bucket; cache entry written by a different block with the same ULID (rebuilt block reusing ULID).
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- invalid remaining size, even after refetch, remaining
- read series
- series
- iterate series
- repaired block is invalid
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/ee36e474d8ab681c.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/bucket.go:3411
if err := bytesLimiter.ReserveWithType(uint64(end-start), SeriesFetched); err != nil {
return httpgrpc.Errorf(int(codes.ResourceExhausted), "exceeded bytes limit while fetching series: %s", err)
}
b, err := r.block.readIndexRange(ctx, int64(start), int64(end-start), r.logger)
if err != nil {
return errors.Wrap(err, "read series range")
}
stats.seriesFetchCount++
stats.SeriesFetchDurationSum += time.Since(begin)
stats.add(SeriesFetched, len(ids), int(end-start))
for i, id := range ids {
c := b[uint64(id)-start:]
l, n := binary.Uvarint(c)
if n < 1 {
return errors.New("reading series length failed")
}
if len(c) < n+int(l) {
if i == 0 && refetch {
return errors.Errorf("invalid remaining size, even after refetch, remaining: %d, expected %d", len(c), n+int(l))
}
// Inefficient, but should be rare.
r.block.metrics.seriesRefetches.WithLabelValues(tenant).Inc()
level.Warn(r.logger).Log("msg", "series size exceeded expected size; refetching", "id", id, "series length", n+int(l), "maxSeriesSize", r.block.estimatedMaxSeriesSize)
// Fetch plus to get the size of next one if exists.
return r.loadSeries(ctx, ids[i:], true, uint64(id), uint64(id)+uint64(n+int(l)+1), bytesLimiter, tenant)
}
c = c[n : n+int(l)]
r.loadedSeriesMtx.Lock()
r.loadedSeries[id] = c
r.loadedSeriesMtx.Unlock()
View on GitHub (pinned to 35b8b99117)