dgraph-io/badger · error
while file.stat on file: %s, error: %v
Error message
while file.stat on file: %s, error: %v
What it means
logFile.Truncate truncates a memtable WAL to a given end offset. Before truncating it stats the file; if f.Stat fails it returns this error with the filename and underlying OS error. It also asserts the DB is not read-only before proceeding.
Source
Thrown at memtable.go:269
path string
// This is a lock on the log file. It guards the fd’s value, the file’s
// existence and the file’s memory map.
//
// Use shared ownership when reading/writing the file or memory map, use
// exclusive ownership to open/close the descriptor, unmap or remove the file.
lock sync.RWMutex
fid uint32
size atomic.Uint32
dataKey *pb.DataKey
baseIV []byte
registry *KeyRegistry
writeAt uint32
opt Options
}
func (lf *logFile) Truncate(end int64) error {
if fi, err := lf.Fd.Stat(); err != nil {
return fmt.Errorf("while file.stat on file: %s, error: %v\n", lf.Fd.Name(), err)
} else if fi.Size() == end {
return nil
}
y.AssertTrue(!lf.opt.ReadOnly)
lf.size.Store(uint32(end))
return lf.MmapFile.Truncate(end)
}
// encodeEntry will encode entry to the buf
// layout of entry
// +--------+-----+-------+-------+
// | header | key | value | crc32 |
// +--------+-----+-------+-------+
func (lf *logFile) encodeEntry(buf *bytes.Buffer, e *Entry, offset uint32) (int, error) {
h := header{
klen: uint32(len(e.Key)),
vlen: uint32(len(e.Value)),
expiresAt: e.ExpiresAt,View on GitHub (pinned to 2a001d466f)
Solutions
- Check the wrapped OS error (%v in the message) to identify why Stat failed (ENOENT, EIO, EBADF)
- Stop external file cleaners from touching the badger directory
- Verify the volume is writable and healthy (dmesg / fsck); a read-only remount after disk errors is common
- Ensure the DB was not opened with ReadOnly:true while writes are expected
Example fix
// before
opt := badger.DefaultOptions(dir)
db, _ := badger.Open(opt) // opened read-only elsewhere, writes attempted
// after
if readOnly {
db, err = badger.Open(opt.WithReadOnly(true)) // read path only; no Truncate
} else {
db, err = badger.Open(opt) // writable
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the volume is writable and DB not opened read-only when writes/truncates are expected
if fi, err := os.Stat(dir); err == nil && fi.Mode()&0o200 == 0 {
return fmt.Errorf("data dir not writable: %s", dir)
} Type guard
func isReadOnlyErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "while file.stat on file")
} Try / catch
if err := runBadger(); err != nil {
if strings.Contains(err.Error(), "while file.stat on file") {
log.Errorf("WAL stat failed (deleted under us / I/O error): %v", err)
// restart process on healthy volume after checking dmesg
}
return err
} Prevention
- Exclude the badger directory from external log-cleaners (logrotate, tmpwatch)
- Monitor dmesg/filesystem errors; read-only remounts after disk failures trigger this
- Open with WithReadOnly(true) only for genuinely read-only workloads
When it happens
Trigger: Calling Truncate indirectly from UpdateSkipList or doneWriting when the underlying WAL file descriptor is bad (file deleted underneath, fd closed, I/O error, or the DB is read-only so the subsequent truncate would assert anyway).
Common situations: File removed by an external cleaner (log rotation, tmpwatch) while badger runs; disk I/O errors; read-only mounts combined with attempts to write; OS-level fd exhaustion leading to bad Stat results.
Related errors
- While setting banned keys: %w
- Unable to do incremental writes because MemTable has data
- Unable to open log dir.
- File %s already exists
- ErrValueLogSize
AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05).
Data as JSON: /api/errors/1b1179e8f37b4241.
Report an issue: GitHub.