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
- Delete or move the existing table file if it is stale (verify via badger info/manifest that it's unreferenced), then retry CreateTable
- Use a fresh/unused table filename (unique table ID) — ensure the directory's next file ID is greater than any existing file
- Recover the DB properly (open with Badger so the manifest is reconciled) instead of re-running flush/compaction manually against the same directory
- 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
- Never reuse table file IDs; ensure new table IDs exceed the max existing ID in the directory
- After a crash, run proper badger recovery instead of re-running flushes into the same dir
- Only one process should write tables to a badger directory at a time
- Before manual rebuilds, check the manifest for references to the target file
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
- Cannot find directory %q for read-only open
- Unable to open log dir.
- Unable to open mem dir.
- File %s already exists
- Invalid filename: %s
AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05).
Data as JSON: /api/errors/6a41d4db2c750567.
Report an issue: GitHub.