dgraph-io/badger · error

Duplicate file found. Please delete one.

Error message

Duplicate file found. Please delete one.

What it means

Thrown by valueLog.populateFilesMap (value.go:509) when two files in the value-log directory resolve to the same uint32 file ID, i.e. duplicate '<id>.vlog' names exist. Since filesMap is keyed by fid, badger cannot disambiguate them and Open() fails.

Solutions

  1. Identify the duplicate ids (ls the dir, strip .vlog, find repeats) and delete the older/smaller one — badger explicitly instructs 'Please delete one'.
  2. Compare file sizes/mtimes and keep the newest complete file; verify with a backup if unsure.
  3. Never merge two badger data directories; restore one coherent snapshot instead.
  4. On case-insensitive filesystems, ensure restores don't create same-id names differing only in case.

Example fix

// before
$ ls vlogdir
5.vlog  05.vlog   # both parse to fid 5
// after
$ rm vlogdir/05.vlog   # or move it away; keep the correct one
$ ls vlogdir
5.vlog
Defensive patterns

Strategy: validation

Validate before calling

// detect duplicate fids before opening badger
func findDuplicateVlogIDs(dir string) (map[uint64]int, error) {
    dups := map[uint64]int{}
    files, err := os.ReadDir(dir)
    if err != nil { return nil, err }
    seen := map[uint64]struct{}{}
    for _, f := range files {
        n := f.Name()
        if !strings.HasSuffix(n, ".vlog") { continue }
        id, err := strconv.ParseUint(n[:len(n)-5], 10, 32)
        if err != nil { continue }
        if _, ok := seen[id]; ok { dups[id]++ }
        seen[id] = struct{}{}
    }
    return dups, nil
}

Type guard

func fidOf(name string) (uint64, bool) {
    if !strings.HasSuffix(name, ".vlog") { return 0, false }
    id, err := strconv.ParseUint(strings.TrimSuffix(name, ".vlog"), 10, 32)
    return id, err == nil
}

Try / catch

dups, _ := findDuplicateVlogIDs(vlogDir)
if len(dups) > 0 {
    for id := range dups {
        keep, drop := pickNewest(filepath.Join(vlogDir, fmt.Sprintf("%d.vlog", id)))
        _ = keep
        os.Remove(drop) // or move to quarantine
    }
}
db, err := badger.Open(opts)

Prevention

When it happens

Trigger: Two files with identical numeric names but different casing/paths that collapse to the same fid (e.g. '5.vlog' and '05.vlog' both parse to 5), or a genuinely duplicated file restored twice under names that hash to the same id.

Common situations: Manual restores where a copy was made as '5 (copy).vlog' renamed back to '5.vlog' alongside the original on a case-insensitive filesystem, backup tooling writing duplicate names, merging two data directories.

Related errors


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

Appendix: source

Thrown at value.go:509

	vlog.filesMap = make(map[uint32]*logFile)

	files, err := os.ReadDir(vlog.dirPath)
	if err != nil {
		return errFile(err, vlog.dirPath, "Unable to open log dir.")
	}

	found := make(map[uint64]struct{})
	for _, file := range files {
		if !strings.HasSuffix(file.Name(), ".vlog") {
			continue
		}
		fsz := len(file.Name())
		fid, err := strconv.ParseUint(file.Name()[:fsz-5], 10, 32)
		if err != nil {
			return errFile(err, file.Name(), "Unable to parse log id.")
		}
		if _, ok := found[fid]; ok {
			return errFile(err, file.Name(), "Duplicate file found. Please delete one.")
		}
		found[fid] = struct{}{}

		lf := &logFile{
			fid:      uint32(fid),
			path:     vlog.fpath(uint32(fid)),
			registry: vlog.db.registry,
		}
		vlog.filesMap[uint32(fid)] = lf
		if vlog.maxFid < uint32(fid) {
			vlog.maxFid = uint32(fid)
		}
	}
	return nil
}

func (vlog *valueLog) createVlogFile() (*logFile, error) {
	fid := vlog.maxFid + 1

View on GitHub (pinned to 2a001d466f)