thanos-io/thanos · error

unknown index header file version

Error message

unknown index header file version %d

What it means

The binary index-header carries a format version byte; the reader only understands BinaryFormatV1. This error is returned when the version byte read from the header is anything other than 1, i.e., the file was written by an unknown future (or bogus) format.

Solutions

  1. Clear the shared cache directory (delete cached .index-header files) so they are regenerated in the format this binary supports.
  2. Run matching Thanos versions across all instances sharing the same cache volume.
  3. If in the bucket, re-upload index-headers generated by a compatible version.
  4. Verify file integrity if the version byte looks randomly corrupted.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

b, err := os.ReadFile(hdrPath)
if err == nil && len(b) >= 5 && int(b[4]) != BinaryFormatV1 {
    os.Remove(hdrPath) // unknown version; clear so it is regenerated in v1
}

Type guard

func isSupportedIndexHeaderVersion(b []byte) bool {
    return len(b) >= 5 && int(b[4]) == BinaryFormatV1
}

Try / catch

r, err := NewBinaryReader(ctx, logger, bkt, dir, id, sampling, metrics)
if err != nil && strings.Contains(err.Error(), "unknown index header file version") {
    // shared cache written by incompatible version; purge and rebuild
    purgeIndexHeaderCache(dir)
    return NewBinaryReader(ctx, logger, bkt, dir, id, sampling, metrics)
}

Prevention

When it happens

Trigger: Loading a binary index-header whose version byte (offset 4) is not BinaryFormatV1 — e.g., a cache file produced by a newer Thanos that bumped the format, or random bytes being misread as an index-header.

Common situations: Rolling upgrade/downgrade of store-gateways sharing a persistent cache volume written by a different format version; corrupted cache where a version byte got flipped; mixing Thanos forks with divergent formats.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/5dbb7a7f7c5e0923. Report an issue: GitHub.

Appendix: source

Thrown at pkg/block/indexheader/binary_reader.go:682

	start := time.Now()

	defer func() {
		r.metrics.loadDuration.Observe(time.Since(start).Seconds())
	}()
	// Verify header.
	if r.b.Len() < headerLen {
		return errors.Wrap(encoding.ErrInvalidSize, "index header's header")
	}
	if m := binary.BigEndian.Uint32(r.b.Range(0, 4)); m != MagicIndex {
		return errors.Errorf("invalid magic number %x", m)
	}
	r.version = int(r.b.Range(4, 5)[0])
	r.indexVersion = int(r.b.Range(5, 6)[0])

	r.indexLastPostingEnd = int64(binary.BigEndian.Uint64(r.b.Range(6, headerLen)))

	if r.version != BinaryFormatV1 {
		return errors.Errorf("unknown index header file version %d", r.version)
	}

	r.toc, err = newBinaryTOCFromByteSlice(r.b)
	if err != nil {
		return errors.Wrap(err, "read index header TOC")
	}

	// TODO(bwplotka): Consider contributing to Prometheus to allow specifying custom number for symbolsFactor.
	r.symbols, err = index.NewSymbols(r.b, r.indexVersion, int(r.toc.Symbols))
	if err != nil {
		return errors.Wrap(err, "read symbols")
	}

	var lastName, lastValue []byte
	if r.indexVersion == index.FormatV1 {
		// Earlier V1 formats don't have a sorted postings offset table, so
		// load the whole offset table into memory.
		r.postingsV1 = map[string]map[string]index.Range{}

View on GitHub (pinned to 35b8b99117)