thanos-io/thanos · error
invalid magic number
Error message
invalid magic number %x for %s
What it means
When Thanos builds a binary index-header for a TSDB block, it reads the first 4 bytes of the block's index file from object storage and expects the Prometheus index magic number 0xBAAAD792 (index.MagicIndex). If the bytes differ, the file is not a valid TSDB index (or is truncated/corrupted), so newChunkedIndexReader aborts instead of parsing garbage. The message names the magic actually found and the index file path.
Solutions
- Verify the object at the reported indexFilepath is a real Prometheus index: first 4 bytes should be ba aa d7 92 (big-endian); download the first bytes and inspect.
- Re-upload or re-sync the block from a healthy source (e.g. re-run thanos block sync / re-upload from compactor) to replace the truncated or wrong object.
- Check the block directory layout: the index must be at <block-id>/index with the block's meta.json alongside; fix bucket/prefix configuration if paths are shifted.
- If a tool rewrote the index, confirm it emits the standard Prometheus index format matching FormatV1/V2 expected by this Thanos version.
- Rule out object-storage proxies/CDNs returning empty or error bodies with 200; fetch the object directly from the storage backend.
Example fix
// before: blindly building index-header for a possibly corrupt block
r, _, err := newChunkedIndexReader(ctx, bkt, indexFilepath, attrs, b)
// after: pre-validate the magic before building the reader
rc, _ := bkt.GetRange(ctx, indexFilepath, 0, 4)
b0, _ := io.ReadAll(rc)
if binary.BigEndian.Uint32(b0) != index.MagicIndex {
return nil, fmt.Errorf("block %s has corrupt index, re-upload", indexFilepath)
}
r, _, err := newChunkedIndexReader(ctx, bkt, indexFilepath, attrs, b) Defensive patterns
Strategy: validation
Validate before calling
func validateIndexMagic(ctx context.Context, bkt objstore.BucketReader, path string) error {
rc, err := bkt.GetRange(ctx, path, 0, 4)
if err != nil { return err }
defer rc.Close()
b, err := io.ReadAll(rc)
if err != nil { return err }
if binary.BigEndian.Uint32(b) != index.MagicIndex {
return fmt.Errorf("%s is not a TSDB index (bad magic)", path)
}
return nil
} Prevention
- Before onboarding blocks, verify each index object starts with magic 0xBAAAD792.
- Use thanos tools bucket verify periodically to catch corrupt/truncated blocks early.
- Ensure uploads of block directories are atomic and complete (upload index last or verify after upload).
- Never point Thanos at buckets written by non-Prometheus index formats.
When it happens
Trigger: Writing a binary index-header (WriteBinary -> newChunkedIndexReader) against a block whose index file in the bucket is missing, truncated (fewer than 4 readable bytes yield a wrong value), a placeholder/zero object, or simply not a Prometheus TSDB index file (e.g. wrong object uploaded to the block's index path).
Common situations: Partial multi-part uploads or failed sync leaving a truncated index object; pointing the bucket at a path containing meta.json or chunks instead of index; manually uploaded or transformed blocks; object storage returning zero-filled bodies on some errors; blocks written by forks/tools that do not use the Prometheus index format.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/3df7f10d8040aa6a.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/indexheader/binary_reader.go:204
}
rc, err := bkt.GetRange(ctx, indexFilepath, 0, index.HeaderLen)
if err != nil {
return nil, 0, errors.Wrapf(err, "get TOC from object storage of %s", indexFilepath)
}
b, err := io.ReadAll(rc)
if err != nil {
runutil.CloseWithErrCapture(&err, rc, "close reader")
return nil, 0, errors.Wrapf(err, "get header from object storage of %s", indexFilepath)
}
if err := rc.Close(); err != nil {
return nil, 0, errors.Wrap(err, "close reader")
}
if m := binary.BigEndian.Uint32(b[0:4]); m != index.MagicIndex {
return nil, 0, errors.Errorf("invalid magic number %x for %s", m, indexFilepath)
}
version := int(b[4:5][0])
if version != index.FormatV1 && version != index.FormatV2 {
return nil, 0, errors.Errorf("not supported index file version %d of %s", version, indexFilepath)
}
ir := &chunkedIndexReader{
ctx: ctx,
path: indexFilepath,
size: uint64(attrs.Size),
bkt: bkt,
}
toc, err := ir.readTOC()
if err != nil {
return nil, 0, errView on GitHub (pinned to 35b8b99117)