thanos-io/thanos · error
new index reader
Error message
new index reader
What it means
Thanos' indexheader.WriteBinary converts a block's index postings into a compact binary index-header file. This error wraps failure to create the chunked index reader over the block's index (via newChunkedIndexReader), i.e. the source index could not be opened/parsed to build the binary header.
Solutions
- Verify the block ID and that index.bin exists and is fully uploaded in the bucket (check meta.json and the block directory)
- Check object storage credentials/permissions for the bucket used to fetch the index
- Re-download or re-sync the block from a healthy replica if index.bin is truncated
- Ensure the block was produced by a supported Prometheus/Thanos index version
Example fix
// before
if err := indexheader.WriteBinary(ctx, bkt, id, filename); err != nil {
return err
}
// after
ok, err := block.Exists(ctx, bkt, id)
if err != nil { return err }
if !ok { return errors.Errorf("block %s does not exist", id) }
if err := block.DownloadIndex(ctx, logger, bkt, id, dir); err != nil {
return errors.Wrap(err, "download index")
}
if err := indexheader.WriteBinary(ctx, bkt, id, filename); err != nil {
return errors.Wrap(err, "write index header")
} Defensive patterns
Strategy: fallback
Validate before calling
ok, err := block.Exists(ctx, bkt, id)
if err != nil { return err }
if !ok { return errors.Errorf("block %s not found in bucket", id) }
if _, err := bkt.Get(ctx, path.Join(id.String(), block.IndexFilename)); err != nil {
return errors.Wrap(err, "index.bin not fetchable")
} Type guard
func indexAvailable(ctx context.Context, bkt objstore.Bucket, id ulid.ULID) bool {
rc, err := bkt.Get(ctx, path.Join(id.String(), block.IndexFilename))
if err != nil { return false }
defer rc.Close()
return true
} Try / catch
hdr, err := indexheader.NewBinaryReader(ctx, logger, bkt, dir, id, cfg)
if err != nil {
if objstore.IsNotFoundError(errors.Cause(err)) {
// block gone: refresh meta or remove from local cache and continue
}
return errors.Wrapf(err, "build index header for %s", id)
} Prevention
- Ensure blocks are fully uploaded (meta.json present) before readers process them
- Check object-store credentials and network before batch index-header builds
- Handle blocks disappearing from the bucket mid-operation; re-sync metadata first
- Pin supported Prometheus index versions in compactor/store
When it happens
Trigger: WriteBinary(ctx, bkt, id, filename) is called (directly or via NewBinaryReader/NewLazyBinaryReader) and newChunkedIndexReader fails: the block's index.bin is missing from the bucket or tmp dir, not downloadable, or its format/version cannot be parsed.
Common situations: Store gateway building an index-header cache for a block whose index.bin was deleted or still uploading; misconfigured bucket credentials/block ID causing fetch failures; index built by an unsupported Prometheus version.
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
- compaction
- sync before first pass of downsampling
- sync before second pass of downsampling
- sync before retention
- retention failed
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/de3ab41a75412ebc.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/indexheader/binary_reader.go:117
// PostingsOffsetTable holds start to the same Postings Offset Table section as index related to this index header.
PostingsOffsetTable uint64
}
// WriteBinary build index header from the pieces of index in object storage, and cached in file if necessary.
func WriteBinary(ctx context.Context, bkt objstore.BucketReader, id ulid.ULID, filename string, downloadDuration prometheus.Histogram) ([]byte, error) {
start := time.Now()
defer func() {
downloadDuration.Observe(time.Since(start).Seconds())
}()
var tmpDir = ""
if filename != "" {
tmpDir = filepath.Dir(filename)
}
parallelBucket := WrapWithParallel(bkt, tmpDir)
ir, indexVersion, err := newChunkedIndexReader(ctx, parallelBucket, id)
if err != nil {
return nil, errors.Wrap(err, "new index reader")
}
tmpFilename := ""
if filename != "" {
tmpFilename = filename + ".tmp"
}
// Buffer for copying and encbuffers.
// This also will control the size of file writer buffer.
buf := make([]byte, 32*1024)
bw, err := newBinaryWriter(id, tmpFilename, buf)
if err != nil {
return nil, errors.Wrap(err, "new binary index header writer")
}
defer runutil.CloseWithErrCapture(&err, bw, "close binary writer for %s", tmpFilename)
if err := bw.AddIndexMeta(indexVersion, ir.toc.PostingsTable); err != nil {
return nil, errors.Wrap(err, "add index meta")
}View on GitHub (pinned to 35b8b99117)