thanos-io/thanos · warning
stat file
Error message
stat file: %s
What it means
Wraps an os.Stat failure on a per-entry path inside the config directory during apply(). After listing entries, the reloader stats each (following symlinks) to determine if it is a directory; a stat error aborts the whole apply pass.
Solutions
- Remove or repair broken symlinks in the config directory (find -xtype l)
- Re-run apply; transient races during atomic renames usually resolve on next poll
- Ensure the process user can stat all entries in the directory
- Avoid deleting entries while the reloader is walking (use atomic rename-in-place instead)
Example fix
// before
os.Symlink("/data/gone.yaml", "/etc/thanos/rules.d/r.yaml")
// after
if _, err := os.Stat("/data/gone.yaml"); err == nil {
os.Symlink("/data/gone.yaml", "/etc/thanos/rules.d/r.yaml")
} Defensive patterns
Strategy: retry
Validate before calling
if err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error {
if err != nil { return err }
return nil
}); err != nil { return err } Type guard
func entryExists(path string) bool {
_, err := os.Stat(path)
return !os.IsNotExist(err)
} Try / catch
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
// entry vanished mid-walk: log and continue to next entry
return nil
}
return errors.Wrapf(err, "stat file: %s", path)
} Prevention
- Use atomic renames instead of delete+create for config swaps
- Clean up dangling symlinks in watched directories (find -xtype l)
- Treat transient stat races as retryable
When it happens
Trigger: An entry in the watched directory disappears between ReadDir and os.Stat (race), or is a broken symlink whose target is gone, or has unreadable parent permissions.
Common situations: Config files being atomically swapped (rename over) while the reloader walks; dangling symlink to a deleted target; NFS stale handles.
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
- getting file info
- create meta fetcher
- create working compact directory
- create working downsample directory
- create dir
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/42db31ddbc2a5f90.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/reloader/reloader.go:446
return errors.Wrap(err, "dir symlink eval")
}
outDir, err := filepath.EvalSymlinks(cfgDir.OutputDir)
if err != nil {
return errors.Wrap(err, "dir symlink eval")
}
cfgDirFiles := map[string]struct{}{}
entries, err := os.ReadDir(walkDir)
if err != nil {
return errors.Wrapf(err, "read dir: %s", walkDir)
}
for _, entry := range entries {
path := filepath.Join(walkDir, entry.Name())
// Make sure to follow a symlink before checking if it is a directory.
targetFile, err := os.Stat(path)
if err != nil {
return errors.Wrapf(err, "stat file: %s", path)
}
if targetFile.IsDir() {
continue
}
if err := hashFile(h, path); err != nil {
return errors.Wrapf(err, "build hash for file: %s", path)
}
outFile := filepath.Join(outDir, targetFile.Name())
cfgDirFiles[outFile] = struct{}{}
if err := r.normalize(path, outFile); err != nil {
return errors.Wrapf(err, "move file: %s", path)
}
}
if r.lastCfgDirFiles[i] != nil {
if !maps.Equal(r.lastCfgDirFiles[i], cfgDirFiles) {View on GitHub (pinned to 35b8b99117)