thanos-io/thanos · error
stat block
Error message
stat block %v
What it means
This error wraps os.Stat failures on an individual block directory path during blockMetasFromOldest. When s.skipCorruptedBlocks is false (default), any entry in the shipper dir whose stat fails aborts the whole Sync. When skipping is enabled, the block is logged and added to failedBlocks instead.
Solutions
- Remove the broken entry (dangling symlink/corrupt dir) from the shipper dir, then re-run Sync.
- Enable skipCorruptedBlocks (if using the Shipper API directly) so bad blocks are logged and skipped instead of failing the sync.
- Fix permissions on the offending subdirectory so stat succeeds.
- Run fsck / verify the volume if entries are corrupted after a crash.
Example fix
// before: one dangling symlink fails the whole sync return nil, nil, errors.Wrapf(err, "stat block %v", dir) // after: construct shipper with skipping enabled shipper.New(..., shipper.WithSkipCorruptedBlocks())
Defensive patterns
Strategy: try-catch
Validate before calling
entries, err := os.ReadDir(shipperDir)
if err != nil {
return err
}
for _, e := range entries {
p := filepath.Join(shipperDir, e.Name())
if _, err := os.Stat(p); err != nil {
log.Printf("removing dangling entry %s: %v", p, err)
os.RemoveAll(p)
}
} Type guard
func entryAccessible(base, name string) bool {
_, err := os.Stat(filepath.Join(base, name))
return err == nil
} Try / catch
if err != nil {
if s.skipCorruptedBlocks {
level.Error(s.logger).Log("msg", "stat block", "err", err, "block", dir)
failedBlocks = append(failedBlocks, n)
continue
}
return nil, nil, errors.Wrapf(err, "stat block %v", dir)
} Prevention
- Enable skipCorruptedBlocks so one bad block directory cannot stall the entire sync.
- Ensure the retention cleaner and the shipper do not race on block dirs.
- Fix uid/gid consistency between processes touching the blocks dir.
- After unclean shutdowns, scan for and remove dangling symlinks/broken entries before restart.
When it happens
Trigger: Calling Shipper.Sync or AreAllBlocksUploaded when an entry inside the shipper dir cannot be stat'ed: a dangling symlink, an entry removed concurrently between ReadDir and Stat, a broken filesystem entry, or permission denial on a subdirectory.
Common situations: Partial block deletion by retention cleanup leaving dangling entries; permission mismatch between Prometheus (creator) and Thanos sidecar (reader) users; corrupted directory entries after unclean shutdown/power loss.
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/539d19769862870a.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/shipper/shipper.go:553
names := make([]string, 0, len(fis))
for _, fi := range fis {
names = append(names, fi.Name())
}
for _, n := range names {
if _, ok := block.IsBlockDir(n); !ok {
continue
}
dir := filepath.Join(s.dir.Name(), n)
fi, err := s.dir.Stat(n)
if err != nil {
if s.skipCorruptedBlocks {
level.Error(s.logger).Log("msg", "stat block", "err", err, "block", dir)
failedBlocks = append(failedBlocks, n)
continue
}
return nil, nil, errors.Wrapf(err, "stat block %v", dir)
}
if !fi.IsDir() {
continue
}
m, err := metadata.ReadFromDir(dir)
if err != nil {
if s.skipCorruptedBlocks {
level.Error(s.logger).Log("msg", "read metadata for block", "err", err, "block", dir)
failedBlocks = append(failedBlocks, n)
continue
}
return nil, nil, errors.Wrapf(err, "read metadata for block %v", dir)
}
metas = append(metas, m)
}
sort.Slice(metas, func(i, j int) bool {
return metas[i].MinTime < metas[j].MinTime
})View on GitHub (pinned to 35b8b99117)