thanos-io/thanos · error
stat
Error message
stat %v
What it means
GatherFileStats stats the block's index file (<blockDir>/index) and wraps the os.Stat error with this message. The index file is a mandatory part of every TSDB block; its absence means the block is incomplete or corrupted on disk.
Solutions
- Verify index exists: ls <blockDir>/index. If missing, the block is incomplete — delete it or re-create it from a healthy replica.
- Check permissions on the block dir and index file.
- Ensure the block is fully written (block ID dir contains meta.json, index, chunks/) before uploading.
- Investigate why Prometheus did not finish writing the block (crash logs, OOM).
Example fix
// before
indexFile, err := os.Stat(filepath.Join(blockDir, IndexFilename))
if err != nil {
return nil, errors.Wrapf(err, "stat %v", filepath.Join(blockDir, IndexFilename))
}
// after
indexPath := filepath.Join(blockDir, IndexFilename)
indexFile, err := os.Stat(indexPath)
if os.IsNotExist(err) {
return nil, errors.Errorf("block %s is incomplete: index file %s missing", blockDir, indexPath)
} else if err != nil {
return nil, errors.Wrapf(err, "stat %v", indexPath)
} Defensive patterns
Strategy: validation
Validate before calling
indexPath := filepath.Join(blockDir, "index")
if fi, err := os.Stat(indexPath); err != nil {
return fmt.Errorf("block %s missing index: %w", blockDir, err)
} else if fi.Size() == 0 {
return fmt.Errorf("block %s has empty index file", blockDir)
} Type guard
func hasIndexFile(blockDir string) bool {
fi, err := os.Stat(filepath.Join(blockDir, "index"))
return err == nil && !fi.IsDir() && fi.Size() > 0
} Try / catch
files, err := block.GatherFileStats(blockDir, hashFunc, logger)
if err != nil && os.IsNotExist(errors.Unwrap(err)) {
return skipIncompleteBlock(blockDir) // quarantine/delete, don't retry
} Prevention
- Only ship blocks whose directory contains meta.json, index and chunks/.
- Investigate Prometheus crashes that leave half-written blocks (OOM, kill -9).
- Do not remove index files manually or via custom cleanup scripts.
- Use block loader / bucket verify tooling to detect incomplete blocks early.
When it happens
Trigger: upload() calls GatherFileStats on a block dir where <blockDir>/index does not exist (never written or deleted) or cannot be stat'ed due to permissions/I-O errors.
Common situations: Incomplete block left by a crashed Prometheus (head compaction interrupted); retention/cleanup removed the index prematurely; wrong data dir; blocks partially restored from backup missing index.
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/bd1b12357c2a71b5.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/block/block.go:354
}
mf := metadata.File{
RelPath: filepath.Join(ChunksDirname, f.Name()),
SizeBytes: fi.Size(),
}
if hf != metadata.NoneFunc && !f.IsDir() {
h, err := metadata.CalculateHash(filepath.Join(blockDir, ChunksDirname, f.Name()), hf, logger)
if err != nil {
return nil, errors.Wrapf(err, "calculate hash %v", filepath.Join(ChunksDirname, f.Name()))
}
mf.Hash = &h
}
res = append(res, mf)
}
indexFile, err := os.Stat(filepath.Join(blockDir, IndexFilename))
if err != nil {
return nil, errors.Wrapf(err, "stat %v", filepath.Join(blockDir, IndexFilename))
}
mf := metadata.File{
RelPath: indexFile.Name(),
SizeBytes: indexFile.Size(),
}
if hf != metadata.NoneFunc {
h, err := metadata.CalculateHash(filepath.Join(blockDir, IndexFilename), hf, logger)
if err != nil {
return nil, errors.Wrapf(err, "calculate hash %v", indexFile.Name())
}
mf.Hash = &h
}
res = append(res, mf)
metaFile, err := os.Stat(filepath.Join(blockDir, MetaFilename))
if err != nil {
return nil, errors.Wrapf(err, "stat %v", filepath.Join(blockDir, MetaFilename))
}View on GitHub (pinned to 35b8b99117)