thanos-io/thanos · error

read index header

Error message

read index header

What it means

NewLazyBinaryReader wraps os.Stat errors (other than not-exist) when checking for the index-header file with 'read index header'. The file's existence cannot even be determined — a filesystem/permission problem, not a missing file.

Solutions

  1. Fix permissions on the block cache directory (chmod/chown so the Thanos process can stat files)
  2. Verify the configured --store.index-cache / data directory path is correct and mounted
  3. Check disk health and mount status in the container/host
  4. If the directory is wrong, correct the configuration and restart
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(filepath.Join(dir, id.String(), block.IndexHeaderFilename)); err != nil && !os.IsNotExist(err) { /* fix fs permissions/mount before constructing reader */ }

Try / catch

r, err := indexheader.NewLazyBinaryReader(...); if err != nil && strings.Contains(err.Error(), "read index header") { check dir permissions; fail fast with actionable message }

Prevention

When it happens

Trigger: NewLazyBinaryReader(dir, id, ..., lazyDownload=false) where os.Stat on <dir>/<block-id>/index-header fails with permission or I/O errors (not IsNotExist).

Common situations: Wrong data directory configured; read-only or damaged local disk; permission changes on the cache directory; container volume mount issues.

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/4d25eb9c9077fbbb. Report an issue: GitHub.

Appendix: source

Thrown at pkg/block/indexheader/lazy_binary_reader.go:113

// (mmap) the index-header; it will be loaded at first Reader function call.
func NewLazyBinaryReader(
	ctx context.Context,
	logger log.Logger,
	bkt objstore.BucketReader,
	dir string,
	id ulid.ULID,
	postingOffsetsInMemSampling int,
	metrics *LazyBinaryReaderMetrics,
	binaryReaderMetrics *BinaryReaderMetrics,
	onClosed func(*LazyBinaryReader),
	lazyDownload bool,
) (*LazyBinaryReader, error) {
	if dir != "" && !lazyDownload {
		indexHeaderFile := filepath.Join(dir, id.String(), block.IndexHeaderFilename)
		// If the index-header doesn't exist we should download it.
		if _, err := os.Stat(indexHeaderFile); err != nil {
			if !os.IsNotExist(err) {
				return nil, errors.Wrap(err, "read index header")
			}

			level.Debug(logger).Log("msg", "the index-header doesn't exist on disk; recreating", "path", indexHeaderFile)

			start := time.Now()
			if _, err := WriteBinary(ctx, bkt, id, indexHeaderFile, binaryReaderMetrics.downloadDuration); err != nil {
				return nil, errors.Wrap(err, "write index header")
			}

			level.Debug(logger).Log("msg", "built index-header file", "path", indexHeaderFile, "elapsed", time.Since(start))
		}
	}

	return &LazyBinaryReader{
		ctx:                         ctx,
		logger:                      logger,
		bkt:                         bkt,
		dir:                         dir,

View on GitHub (pinned to 35b8b99117)