dgraph-io/badger · error

file already exists: %s

Error message

file already exists: %s

What it means

CreateTable builds a new sstable by opening the file with O_EXCL, which fails when the file already exists (z.NewFile). In that case CreateTable returns 'file already exists: <fname>' instead of overwriting, protecting existing table data from being clobbered.

Source

Thrown at table/table.go:253

}

func (b *Block) verifyCheckSum() error {
	cs := &pb.Checksum{}
	if err := proto.Unmarshal(b.checksum, cs); err != nil {
		return y.Wrapf(err, "unable to unmarshal checksum for block")
	}
	return y.VerifyChecksum(b.data, cs)
}

func CreateTable(fname string, builder *Builder) (*Table, error) {
	bd := builder.Done()
	mf, err := z.OpenMmapFile(fname, os.O_CREATE|os.O_RDWR|os.O_EXCL, bd.Size)
	if err == z.NewFile {
		// Expected.
	} else if err != nil {
		return nil, y.Wrapf(err, "while creating table: %s", fname)
	} else {
		return nil, fmt.Errorf("file already exists: %s", fname)
	}

	written := bd.Copy(mf.Data)
	y.AssertTrue(written == len(mf.Data))
	if err := z.Msync(mf.Data); err != nil {
		return nil, y.Wrapf(err, "while calling msync on %s", fname)
	}
	return OpenTable(mf, *builder.opts)
}

// OpenTable assumes file has only one table and opens it. Takes ownership of fd upon function
// entry. Returns a table with one reference count on it (decrementing which may delete the file!
// -- consider t.Close() instead). The fd has to writeable because we call Truncate on it before
// deleting. Checksum for all blocks of table is verified based on value of chkMode.
func OpenTable(mf *z.MmapFile, opts Options) (*Table, error) {
	// BlockSize is used to compute the approximate size of the decompressed
	// block. It should not be zero if the table is compressed.
	if opts.BlockSize == 0 && opts.Compression != options.None {

View on GitHub (pinned to 2a001d466f)

Solutions

  1. Delete or move the existing table file if it is stale (verify via badger info/manifest that it's unreferenced), then retry CreateTable
  2. Use a fresh/unused table filename (unique table ID) — ensure the directory's next file ID is greater than any existing file
  3. Recover the DB properly (open with Badger so the manifest is reconciled) instead of re-running flush/compaction manually against the same directory
  4. For createAndOpen-style callers, treat this as expected when the table is already present and fall back to OpenTable

Example fix

// before
t, err := CreateTable(mf, bd) // panics flow with 'file already exists: 000123.sst'
// after
if _, statErr := os.Stat(fname); statErr == nil {
    return OpenTable(mf, table.Options{}) // table already built, just open it
}
t, err := CreateTable(mf, bd)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := os.Stat(fname); err == nil {
	// file already exists — decide: open it or remove it
	return nil, fmt.Errorf("table %s already present, refusing to rebuild", fname)
}

Type guard

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

Try / catch

mf, err := z.OpenMmapFile(fname, os.O_CREATE|os.O_RDWR|os.O_EXCL, bd.Size)
if err == z.NewFile {
	// already exists: open the existing table instead
	return OpenTable(mf, opts)
} else if err != nil {
	return nil, y.Wrapf(err, "while creating table: %s", fname)
}

Prevention

When it happens

Trigger: Calling CreateTable (directly or via flush/compaction paths like handleMemTableFlush, buildTable, createAndOpen) with a filename whose numbered table file already exists on disk — e.g. replaying an old memtable flush, re-running a compaction that picks the same table ID, or a crashed previous run leaving the file behind.

Common situations: Recovery after a crash mid-flush where the sstable was created but the manifest wasn't updated; rebuilding tables from a backup into a directory that already contains tables; concurrent processes writing tables to the same directory; manually re-running tools like buildTable on existing data.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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