clockworklabs/SpacetimeDB · error · io::Error

AlreadyExists

AlreadyExists

Error message

repo {}: segment {} already exists and is non-empty

What it means

create_segment found the target segment file already on disk with length > 0 and refuses to overwrite it (AlreadyExists). Zero-length crash remnants ARE silently overwritten; only files that actually contain data are protected. The guard exists so re-initializing a used directory can never silently destroy committed data.

Source

Thrown at crates/commitlog/src/repo/fs.rs:258

        let lock_path = path.0.with_extension("lock");
        let _lock = scopeguard::guard(
            lockfile::advisory::LockedFile::lock(&lock_path)
                .map_err(|e| io::Error::new(e.source.kind(), format!("repo {}: {}: {}", self, e, e.source)))?,
            |lockfile| {
                if let Err(e) = lockfile.release(true) {
                    // It's ok if removing the file fails, but print a warning
                    // anyways.
                    warn!("repo {}: failed to remove {}: {}", self, lock_path.display(), e);
                }
            },
        );

        // Check whether the segment already exists.
        // Overwrite it if its length is zero.
        match fs::metadata(&path) {
            Ok(stat) => {
                if stat.len() > 0 {
                    return Err(io::Error::new(
                        io::ErrorKind::AlreadyExists,
                        format!("repo {}: segment {} already exists and is non-empty", self, offset),
                    ));
                }
            }
            Err(e) => {
                if e.kind() != io::ErrorKind::NotFound {
                    return Err(io::Error::new(
                        e.kind(),
                        format!(
                            "repo {}: error getting file metadata for segment {}: {}",
                            self, offset, e
                        ),
                    ));
                }
            }
        }

View on GitHub (pinned to 524b4487d9)

Solutions

  1. If you meant to continue the existing log, open it normally - the library resumes existing segments instead of re-creating them
  2. If you really want an empty log, move or delete the old directory contents first (deliberately, after confirming nothing needs them)
  3. Verify no second writer/process is running against the same directory

Example fix

// before: reusing a dirty directory for a fresh log
let log = Commitlog::open(old_dir, Options::default(), None)?; // AlreadyExists

// after: give each log its own directory
let dir = CommitLogDir::new(root.join("log-2026-08-16"))?;
let log = Commitlog::open(dir, Options::default(), None)?;
Defensive patterns

Strategy: validation

Validate before calling

// refuse to initialize into a dirty directory
let is_empty = dir.as_path().read_dir()?.next().is_none();
if !is_empty {
    // open/resume the existing log instead of re-creating, or fail loudly
    return Err(io::Error::new(io::ErrorKind::AlreadyExists, "log directory is not empty"));
}

Type guard

fn is_nonempty_segment_exists(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::AlreadyExists
        && e.to_string().contains("already exists and is non-empty")
}

Try / catch

let log = match Commitlog::open(dir, opts, None) {
    Ok(log) => log,
    Err(e) if is_nonempty_segment_exists(&e) => {
        // the directory already holds a log: resume it or pick another directory
        return Err(e);
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Creating a segment whose base offset already has a non-empty file on disk: re-opening or re-initializing a Commitlog in a directory that already holds segments, running a second instance against the same directory, or manually copying segment files into the directory.

Common situations: Reusing a data directory for a 'fresh' log; double-starting a service; restoring files into a directory a new log intends to use.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/50ab5d8b35921f7f. Report an issue: GitHub.