{"record":{"id":"9575cf8c1c939552","repo":"thanos-io/thanos","slug":"getting-file-info-v","errorCode":null,"errorMessage":"getting file info %v","messagePattern":"getting file info (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/block/block.go","lineNumber":335,"sourceCode":"\n\t// ReadDir returns files in sorted order already.\n\tvar result []string\n\tfor _, f := range files {\n\t\tresult = append(result, f.Name())\n\t}\n\treturn result\n}\n\n// GatherFileStats returns metadata.File entry for files inside TSDB block (index, chunks, meta.json).\nfunc GatherFileStats(blockDir string, hf metadata.HashFunc, logger log.Logger) (res []metadata.File, _ error) {\n\tfiles, err := os.ReadDir(filepath.Join(blockDir, ChunksDirname))\n\tif err != nil {\n\t\treturn nil, errors.Wrapf(err, \"read dir %v\", filepath.Join(blockDir, ChunksDirname))\n\t}\n\tfor _, f := range files {\n\t\tfi, err := f.Info()\n\t\tif err != nil {\n\t\t\treturn nil, errors.Wrapf(err, \"getting file info %v\", filepath.Join(ChunksDirname, f.Name()))\n\t\t}\n\n\t\tmf := metadata.File{\n\t\t\tRelPath:   filepath.Join(ChunksDirname, f.Name()),\n\t\t\tSizeBytes: fi.Size(),\n\t\t}\n\t\tif hf != metadata.NoneFunc && !f.IsDir() {\n\t\t\th, err := metadata.CalculateHash(filepath.Join(blockDir, ChunksDirname, f.Name()), hf, logger)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, errors.Wrapf(err, \"calculate hash %v\", filepath.Join(ChunksDirname, f.Name()))\n\t\t\t}\n\t\t\tmf.Hash = &h\n\t\t}\n\t\tres = append(res, mf)\n\t}\n\n\tindexFile, err := os.Stat(filepath.Join(blockDir, IndexFilename))\n\tif err != nil {","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/thanos-io/thanos/blob/35b8b991177def87ed52dcf10f9b6d87f07282c8/pkg/block/block.go#L317-L353","documentation":"While iterating entries of the chunks/ directory, GatherFileStats calls f.Info() to get os.FileInfo and wraps its error with this message. It fails when a directory entry cannot be stat'ed — typically because the file was removed between ReadDir and Info (lstat on a dangling entry) or an I/O error occurred.","triggerScenarios":"A chunk file inside <blockDir>/chunks is deleted (e.g., concurrent cleanup/compaction) after ReadDir listed it, or the entry can't be stat'ed due to filesystem/I/O errors.","commonSituations":"Concurrent deletion of the block while uploading; node with flaky disks; sticky/permission issues preventing lstat on a chunk file.","solutions":["Stop concurrent processes from deleting the block while GatherFileStats runs (e.g., don't upload blocks still owned by Prometheus retention).","Re-scan the directory; if the block is being actively compacted, retry after compaction completes.","Check filesystem health (dmesg, fsck) if lstat errors persist.","Verify permissions allow lstat on all chunk files."],"exampleFix":"// before\nfi, err := f.Info()\nif err != nil {\n\treturn nil, errors.Wrapf(err, \"getting file info %v\", filepath.Join(ChunksDirname, f.Name()))\n}\n// after\nfi, err := f.Info()\nif err != nil {\n\tif os.IsNotExist(err) {\n\t\tcontinue // file vanished mid-scan; skip it\n\t}\n\treturn nil, errors.Wrapf(err, \"getting file info %v\", filepath.Join(ChunksDirname, f.Name()))\n}","handlingStrategy":"retry","validationCode":"entries, err := os.ReadDir(filepath.Join(blockDir, \"chunks\"))\nif err != nil { return err }\nfor _, e := range entries {\n\tif _, err := e.Info(); err != nil {\n\t\treturn fmt.Errorf(\"chunk entry %s unreadable pre-upload: %w\", e.Name(), err)\n\t}\n}","typeGuard":null,"tryCatchPattern":"files, err := block.GatherFileStats(blockDir, hashFunc, logger)\nif err != nil {\n\t// transient race with deletion: retry once after short delay\n\terr = retryWithBackoff(ctx, 3, 500*time.Millisecond, func() error {\n\t\tfiles, err = block.GatherFileStats(blockDir, hashFunc, logger)\n\t\treturn err\n\t})\n}","preventionTips":["Avoid uploading blocks that may be concurrently compacted or retention-deleted.","Run uploads and data cleanup from the same lifecycle owner (sidecar watches Prometheus).","Skip retries when the block dir itself is gone (OS.IsNotExist on the parent).","Monitor for duplicated cleanup jobs racing on shared volumes."],"tags":["filesystem","thanos","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"}