dgraph-io/badger · warning
Unable to find fid: %d
Error message
Unable to find fid: %d
What it means
Thrown by valueLog.rewrite (value.go:364) during GC (doRunGC): after moving surviving entries to new log files, the code removes the old file from filesMap but a sanity check finds f.fid is no longer (or never was) present, so the GC run returns this error instead of proceeding.
Source
Thrown at value.go:364
// Decrease the batch size to half.
batchSize = batchSize / 2
continue
}
return err
}
i += batchSize
}
vlog.opt.Infof("Processed %d entries in %d loops", len(wb), loops)
vlog.opt.Infof("Total entries: %d. Moved: %d", count, moved)
vlog.opt.Infof("Removing fid: %d", f.fid)
var deleteFileNow bool
// Entries written to LSM. Remove the older file now.
{
vlog.filesLock.Lock()
// Just a sanity-check.
if _, ok := vlog.filesMap[f.fid]; !ok {
vlog.filesLock.Unlock()
return fmt.Errorf("Unable to find fid: %d", f.fid)
}
if vlog.iteratorCount() == 0 {
delete(vlog.filesMap, f.fid)
deleteFileNow = true
} else {
vlog.filesToBeDeleted = append(vlog.filesToBeDeleted, f.fid)
}
vlog.filesLock.Unlock()
}
if deleteFileNow {
if err := vlog.deleteLogFile(f); err != nil {
return err
}
}
return nil
}
View on GitHub (pinned to 2a001d466f)
Solutions
- Treat it as a benign end-state in GC loops: catch it and skip that fid, re-listing vlog files for the next GC candidate.
- Ensure only one goroutine runs value-log GC at a time (serialize with a mutex or worker).
- Re-run GC later on a still-present file id obtained from vlog.filesMap/lf, rather than a stale cached fid.
- Do not delete .vlog files externally while the DB is open; let badger's GC delete them.
- If persistent, restart the DB to rebuild filesMap and retry GC.
Example fix
// before
err := db.RunValueLogGC(0.5)
if err != nil { log.Fatal(err) }
// after
err := db.RunValueLogGC(0.5)
if err != nil && !strings.Contains(err.Error(), "Unable to find fid") && !errors.Is(err, badger.ErrRejected) {
log.Fatal(err)
} // otherwise: file already rewritten/removed; retry GC on next cycle Defensive patterns
Strategy: try-catch
Validate before calling
// before GC, check the file still exists in the live set
info, err := db.Opts() // or track fids via value log registry
// simplest guard: only run GC sequentially from a single worker goroutine
var gcMu sync.Mutex
func safeRunGC(db *badger.DB, ratio float64) error {
gcMu.Lock(); defer gcMu.Unlock()
return db.RunValueLogGC(ratio)
} Type guard
func isFidNotFoundErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "Unable to find fid")
} Try / catch
err := db.RunValueLogGC(0.5)
switch {
case err == nil:
// GC done
case errors.Is(err, badger.ErrNoRewrite), isFidNotFoundErr(err):
// file already rewritten/removed: benign, skip and pick another fid later
default:
return err
} Prevention
- Run value-log GC from a single dedicated goroutine; never concurrently.
- Persist/retry using current file ids, not stale ids cached across restarts.
- Never delete *.vlog files manually while the DB is open.
- Handle ErrNoRewrite-like outcomes as normal GC flow, not fatal errors.
When it happens
Trigger: Running vlog.RunGC/DB.RunValueLogGC when the target log file was already deleted or replaced by a concurrent GC/iterator cleanup, or after external manipulation/removal of .vlog files while the DB is open.
Common situations: Two GC loops racing on the same value-log file, calling RunValueLogGC on an already-collected file id, operator deleting a .vlog file mid-run, crash-recovery removing files between selection and rewrite.
Related errors
- ErrNoRewrite
- ErrRejected
- Cannot use SetDiscardTs with managedDB=false.
- Empty value: %+v
- Unable to parse log id.
AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05).
Data as JSON: /api/errors/e04a4c2df0817c3f.
Report an issue: GitHub.