clockworklabs/SpacetimeDB · error · io::Error

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

Error message

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

What it means

When the streaming writer rolls into a new segment it creates the segment file; creation returned AlreadyExists and the existing file is longer than a bare segment header, i.e. it already contains commits. Only header-only stubs are automatically removed and retried; real data is never silently overwritten, so the operation fails with this error naming the repo and segment offset.

Source

Thrown at crates/commitlog/src/stream/writer.rs:466

    let segment_offset = last_written_tx_range
        .as_ref()
        .map(|range| range.end)
        .unwrap_or_default();
    let mut segment = loop {
        match repo.create_segment(segment_offset, header) {
            Ok(segment) => break segment,
            Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
                trace!("segment already exists");
                let mut s = repo.open_segment_writer(segment_offset)?;
                let len = s.segment_len()?;
                trace!("segment len: {len}");
                if len <= segment::Header::LEN as _ {
                    trace!("overwriting existing segment");
                    repo.remove_segment(segment_offset)?;
                    continue;
                }

                return Err(io::Error::new(
                    e.kind(),
                    format!(
                        "repo {}: segment {} already exists and is non-empty: {}",
                        repo, segment_offset, e
                    ),
                ));
            }
            Err(e) => return Err(e),
        }
    };
    fallocate(&mut segment, &commitlog_options)?;

    let index_writer = repo
        .create_offset_index(segment_offset, commitlog_options.offset_index_len())
        .inspect_err(|e| warn!("unable to create offset index segment={segment_offset} err={e:?}"))
        .map(|index| OffsetIndexWriter::new(index, commitlog_options))
        .ok();

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Clear or move aside the existing commitlog directory before re-bootstrapping.
  2. Start the replay at a segment offset beyond the segments already present.
  3. If the existing data is confirmed disposable, delete the named segment file and retry.
  4. Ensure exactly one writer process per commitlog repo.
Defensive patterns

Strategy: validation

Validate before calling

// Before replay, scan the target repo for existing segments overlapping the range:
for seg_offset in existing_segment_offsets(repo)? {
    if replay_range.contains(&seg_offset) {
        return Err(format!("repo already holds segment {} in replay range", seg_offset));
    }
}

Try / catch

match writer.append_all(stream, progress).await {
    Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
        // Decide: clear the repo and restart replay, or move the resume point past
        // the existing segment. Never blind-retry - the file will still be there.
    }
    r => r,
}

Prevention

When it happens

Trigger: Replaying a bootstrap/replication stream into a commitlog repo that already contains segments covering the same offset range; running two writers against the same repo concurrently; restoring an old data directory and replaying overlapping history.

Common situations: Re-running follower bootstrap without clearing the data directory; a previous partial replay left segments behind; two nodes misconfigured to share one commitlog path.

Related errors


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