{"record":{"id":"42db31ddbc2a5f90","repo":"thanos-io/thanos","slug":"stat-file-s","errorCode":null,"errorMessage":"stat file: %s","messagePattern":"stat file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/reloader/reloader.go","lineNumber":446,"sourceCode":"\t\t\treturn errors.Wrap(err, \"dir symlink eval\")\n\t\t}\n\t\toutDir, err := filepath.EvalSymlinks(cfgDir.OutputDir)\n\t\tif err != nil {\n\t\t\treturn errors.Wrap(err, \"dir symlink eval\")\n\t\t}\n\n\t\tcfgDirFiles := map[string]struct{}{}\n\t\tentries, err := os.ReadDir(walkDir)\n\t\tif err != nil {\n\t\t\treturn errors.Wrapf(err, \"read dir: %s\", walkDir)\n\t\t}\n\t\tfor _, entry := range entries {\n\t\t\tpath := filepath.Join(walkDir, entry.Name())\n\n\t\t\t// Make sure to follow a symlink before checking if it is a directory.\n\t\t\ttargetFile, err := os.Stat(path)\n\t\t\tif err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"stat file: %s\", path)\n\t\t\t}\n\n\t\t\tif targetFile.IsDir() {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif err := hashFile(h, path); err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"build hash for file: %s\", path)\n\t\t\t}\n\n\t\t\toutFile := filepath.Join(outDir, targetFile.Name())\n\t\t\tcfgDirFiles[outFile] = struct{}{}\n\t\t\tif err := r.normalize(path, outFile); err != nil {\n\t\t\t\treturn errors.Wrapf(err, \"move file: %s\", path)\n\t\t\t}\n\t\t}\n\t\tif r.lastCfgDirFiles[i] != nil {\n\t\t\tif !maps.Equal(r.lastCfgDirFiles[i], cfgDirFiles) {","sourceCodeStart":428,"sourceCodeEnd":464,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/reloader/reloader.go#L428-L464","documentation":"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.","triggerScenarios":"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.","commonSituations":"Config files being atomically swapped (rename over) while the reloader walks; dangling symlink to a deleted target; NFS stale handles.","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)"],"exampleFix":"// before\nos.Symlink(\"/data/gone.yaml\", \"/etc/thanos/rules.d/r.yaml\")\n// after\nif _, err := os.Stat(\"/data/gone.yaml\"); err == nil {\n    os.Symlink(\"/data/gone.yaml\", \"/etc/thanos/rules.d/r.yaml\")\n}","handlingStrategy":"retry","validationCode":"if err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error {\n    if err != nil { return err }\n    return nil\n}); err != nil { return err }","typeGuard":"func entryExists(path string) bool {\n    _, err := os.Stat(path)\n    return !os.IsNotExist(err)\n}","tryCatchPattern":"if _, err := os.Stat(path); err != nil {\n    if os.IsNotExist(err) {\n        // entry vanished mid-walk: log and continue to next entry\n        return nil\n    }\n    return errors.Wrapf(err, \"stat file: %s\", path)\n}","preventionTips":["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"],"tags":["filesystem","symlink","race-condition"],"backgroundTag":"file-not-found","analyzedSha":"35b8b991177def87ed52dcf10f9b6d87f07282c8","analyzedAt":"2026-09-07T01:49:59.689Z","contentChangedAt":"2026-09-07T01:49:59.689Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}