thanos-io/thanos · error
reading
Error message
reading
What it means
getInt32 reads exactly 4 bytes from an index postings reader; io.ReadFull failures (EOF, closed reader) are wrapped as "reading". It signals the postings binary stream ended prematurely or the underlying reader broke while decoding a posting count.
Solutions
- Re-download/repair the block index (delete the cached partial file and re-sync).
- Verify block integrity with thanos tools bucket verify.
- Check that the underlying reader/stream is not closed while reading.
- Inspect the wrapped error (io.EOF vs io.ErrUnexpectedEOF vs syscall errors) for the exact cause.
Example fix
// before // reuse of possibly corrupt cached index // after os.Remove(cacheIndexFile) // force re-download and re-verify checksum store.SyncBlocks(ctx)
Defensive patterns
Strategy: fallback
Validate before calling
// validate index integrity before decoding
if err := verifyIndexChecksum(indexPath); err != nil { reDownloadIndex() } Try / catch
if err != nil && strings.Contains(err.Error(), "reading") {
if errors.Is(errors.Unwrap(err), io.EOF) || errors.Is(errors.Unwrap(err), io.ErrUnexpectedEOF) {
// treat as corrupt/truncated index: re-fetch block
}
} Prevention
- Verify block checksums after download (bucket verify)
- Delete partial caches on failed downloads
- Alert on index-corruption metrics from store-gateway
When it happens
Trigger: postingsReaderBuilder.Next reading from a truncated/corrupted postings file, a closed connection/stream, or an EOF at the end of malformed data.
Common situations: Corrupted block index files in object storage; interrupted downloads leaving partial index data; reading a buffer/stream that was closed early.
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/1b18b0fda74b4e0a.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/store/postings.go:54
// and builds a diff varint encoded []byte that could be later used directly.
func newPostingsReaderBuilder(ctx context.Context, r *bufio.Reader, postings []postingPtr, start, length int64) *postingsReaderBuilder {
prb := &postingsReaderBuilder{
r: r,
readBuf: make([]byte, 4),
start: start,
length: length,
postings: postings,
uvarintEncodeBuf: make([]byte, binary.MaxVarintLen64),
ctx: ctx,
}
return prb
}
func getInt32(r io.Reader, buf []byte) (uint32, error) {
read, err := io.ReadFull(r, buf)
if err != nil {
return 0, errors.Wrap(err, "reading")
}
if read != 4 {
return 0, fmt.Errorf("read got %d bytes instead of 4", read)
}
return binary.BigEndian.Uint32(buf), nil
}
func (r *postingsReaderBuilder) Next() bool {
if r.ctx.Err() != nil {
r.e = r.ctx.Err()
return false
}
if r.repeatFor > 0 {
r.keyID = r.postings[r.pi-r.repeatFor].keyID
r.repeatFor--
return true
}
if r.pi >= len(r.postings) {View on GitHub (pinned to 35b8b99117)