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

  1. Check the wrapped OS error (%v in the message) to identify why Stat failed (ENOENT, EIO, EBADF)
  2. Stop external file cleaners from touching the badger directory
  3. Verify the volume is writable and healthy (dmesg / fsck); a read-only remount after disk errors is common
  4. 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

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


AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05). Data as JSON: /api/errors/1b1179e8f37b4241. Report an issue: GitHub.