thanos-io/thanos · error
get postings offset entry
Error message
get postings offset entry
What it means
Wrapped decoder error from PostingsOffset: while iterating the postings offset table via index.Decoder to find ranges for a name/value pair, the decoder reported d.Err() (malformed uvarint data or unexpected EOF).
Solutions
- Rebuild the index-header file (delete from bucket, let Thanos regenerate)
- Verify the block index integrity and re-upload/re-compact if corrupted
- Restore the block from a backup if storage corruption is confirmed
Defensive patterns
Strategy: try-catch
Try / catch
rngs, err := br.PostingsOffset(name, value); if err != nil && strings.Contains(err.Error(), "get postings offset entry") { fall back to full index postings reader or rebuild header } Prevention
- Rebuild index-header files on integrity doubts
- Verify block checksums before serving queries
- Alert on repeated decode errors from the same block
When it happens
Trigger: Calling Reader.PostingsOffset(name, value) when the sampled postings table bytes are corrupt/truncated so the decode loop hits a decoding error before finding or exhausting entries.
Common situations: Corrupt index files in storage; index-header built from an index that later changed; bit rot or partial transfer of block data.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/0d567c4d508b2356.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/indexheader/binary_reader.go:977
// Let's exit and do binary search again / exit if nothing wanted.
if len(newSameRngs) > 0 {
// We added some ranges in this iteration. Use next posting offset as the end of our ranges.
// We know it exists as we never go further in this loop than e.offsets[i, i+1).
skipNAndName(&d, &buf)
d.UvarintBytes() // Label value.
postingOffset := int64(d.Uvarint64())
for j := range newSameRngs {
newSameRngs[j].End = postingOffset - crc32.Size
}
rngs = append(rngs, newSameRngs...)
}
break
}
if d.Err() != nil {
return nil, errors.Wrap(d.Err(), "get postings offset entry")
}
}
return rngs, nil
}
func (r *BinaryReader) LookupSymbol(ctx context.Context, o uint32) (string, error) {
if r.indexVersion == index.FormatV1 {
// For v1 little trick is needed. Refs are actual offset inside index, not index-header. This is different
// of the header length difference between two files.
o += headerLen - index.HeaderLen
}
if s, ok := r.nameSymbols[o]; ok {
return s, nil
}
cacheIndex := o % valueSymbolsCacheSizeView on GitHub (pinned to 35b8b99117)