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
- If you meant to continue the existing log, open it normally - the library resumes existing segments instead of re-creating them
- If you really want an empty log, move or delete the old directory contents first (deliberately, after confirming nothing needs them)
- 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
- One directory per log instance; never reuse data dirs for new logs
- Treat AlreadyExists from open as a configuration bug, not a retryable error
- Automate directory provisioning so operators cannot point two logs at one path
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.