thanos-io/thanos · error
index header PostingsOffset
Error message
index header PostingsOffset
What it means
When the requested posting list is not in cache, bucketIndexReader asks the block's index header for the byte offset of the postings table for a key. Wrapping an error from that lookup means the index header could not be consulted (or the offset could not be resolved) for postingPtr. This typically indicates the index header itself failed to load or the postings entry referenced does not exist in the block's header.
Solutions
- Re-sync the store-gateway bucket index so block metadata matches what is actually in object storage
- Check the underlying wrapped error: if the block is missing/deleted, drop it from local view and retry the query
- Re-upload or restore the block's index (meta.json/index) if the header is corrupted in the bucket
- Ensure block deletion uses proper two-step (deletion-mark) flow so readers stop using the block before removal
Defensive patterns
Strategy: retry
Validate before calling
if _, ok := r.block.indexHeader.GetPostingsOffset(name, value); !ok {
// postings offset absent: resync block metadata or skip this block
} Type guard
func blockServesPosting(ih index.Header, name, value string) bool { _, ok := ih.GetPostingsOffset(name, value); return ok } Try / catch
output, closeFns, err := reader.Postings(ctx, tenant, postingGroups...)
if err != nil && strings.Contains(err.Error(), "index header PostingsOffset") {
if rerr := storeGateway.ResyncBlocks(ctx); rerr == nil {
output, closeFns, err = reader.Postings(ctx, tenant, postingGroups...) // retry once after resync
}
} Prevention
- Keep store-gateway bucket index resync enabled with short intervals
- Use deletion marks instead of hard-deleting blocks served by readers
- Monitor for blocks listed in meta but missing from the bucket
- Validate index header uploads complete before advertising blocks
When it happens
Trigger: Calling Postings() with a label name/value whose offset lookup in the loaded index header fails: index header not yet loaded/refreshed for the block, block was deleted or replaced in object storage mid-query, or a corrupted/unsupported index header binary.
Common situations: Store-gateway serving a block whose meta was removed from the bucket between listing and querying; race between block removal (compaction) and in-flight queries; corrupted index header file in object storage after a failed upload.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- get object attributes of
- invalid magic number
- write index header
- generate index header
- create index header reader
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/ef0161a93310ffb6.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/bucket.go:3166
l, closer, err := r.decodeCachedPostings(b)
if err != nil {
return nil, closeFns, errors.Wrap(err, "decode postings")
}
output[ix] = l
closeFns = append(closeFns, closer...)
continue
}
// Cache miss; save pointer for actual posting in index stored in object store.
ptr, err := r.block.indexHeaderReader.PostingsOffset(key.Name, key.Value)
if err == indexheader.NotFoundRangeErr {
// This block does not have any posting for given key.
output[ix] = index.EmptyPostings()
continue
}
if err != nil {
return nil, closeFns, errors.Wrap(err, "index header PostingsOffset")
}
r.stats.postingsToFetch++
ptrs = append(ptrs, postingPtr{ptr: ptr, keyID: ix})
}
sort.Slice(ptrs, func(i, j int) bool {
return ptrs[i].ptr.Start < ptrs[j].ptr.Start
})
// TODO(bwplotka): Asses how large in worst case scenario this can be. (e.g fetch for AllPostingsKeys)
// Consider sub split if too big.
parts := r.block.partitioner.Partition(len(ptrs), func(i int) (start, end uint64) {
return uint64(ptrs[i].ptr.Start), uint64(ptrs[i].ptr.End)
})
size = 0
for _, part := range parts {View on GitHub (pinned to 35b8b99117)