clockworklabs/SpacetimeDB · error · io::Error

segment {offset} already exists

Error message

segment {offset} already exists

What it means

The in-memory commitlog Repo implementation (crates/dst/src/sim/commitlog.rs) stores segments in a BTreeMap keyed by u64 offset. create_segment returns AlreadyExists when a segment at that offset is already present AND non-empty. Re-creating a segment that exists but is still empty is deliberately allowed (treated as reclaiming crash-recovery scratch space) via Segment::from_shared on the existing storage.

Source

Thrown at crates/dst/src/sim/commitlog.rs:174

impl fmt::Display for Memory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("<dst-memory>")
    }
}

impl Repo for Memory {
    type SegmentWriter = Segment;
    type SegmentReader = ReadOnlySegment;

    fn create_segment(&self, offset: u64, header: Header) -> io::Result<Self::SegmentWriter> {
        let mut inner = self.segments.write().unwrap();
        let mut segment = match inner.entry(offset) {
            btree_map::Entry::Occupied(entry) => {
                let entry = entry.get();
                if entry.read().unwrap().is_empty() {
                    Segment::from_shared(self.space.clone(), entry.clone())
                } else {
                    return Err(io::Error::new(
                        io::ErrorKind::AlreadyExists,
                        format!("segment {offset} already exists"),
                    ));
                }
            }
            btree_map::Entry::Vacant(entry) => {
                let storage = entry.insert(Arc::new(RwLock::new(Storage::new())));
                Segment::from_shared(self.space.clone(), storage.clone())
            }
        };
        header.write(&mut segment)?;

        Ok(segment)
    }

    fn open_segment_reader(&self, offset: u64) -> io::Result<Self::SegmentReader> {
        self.open_segment_writer(offset).map(Into::into)
    }

View on GitHub (pinned to 6dee26c6ef)

Solutions

  1. If the segment already exists and you want to reuse it, call open_segment_writer(offset) instead of create_segment
  2. Call remove_segment(offset) first when you truly want to recreate it from scratch
  3. Reset or recreate the Memory repo between replays/tests so state does not leak
  4. Check your offset math — segments are keyed by exact u64 offset, so an off-by-one lands on a neighbor

Example fix

// before
let seg = repo.create_segment(offset, header)?; // AlreadyExists on replay

// after
let seg = match repo.create_segment(offset, header.clone()) {
    Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => repo.open_segment_writer(offset)?,
    Ok(seg) => seg,
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Memory repo exposes no exists(); approximate by probing a reader:
fn segment_exists(repo: &Memory, offset: u64) -> bool {
    matches!(repo.open_segment_reader(offset), Ok(_))
}

Try / catch

let segment = match repo.create_segment(offset, header.clone()) {
    Ok(seg) => seg,
    Err(ref e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
        repo.open_segment_writer(offset)? // reuse existing segment
    }
    Err(e) => return Err(e.into()),
};

Prevention

When it happens

Trigger: Calling Repo::create_segment(offset, header) twice for the same offset when the first segment has had bytes written or its header written; a log replay/recovery routine that unconditionally recreates segments it discovers; two writers racing to create the same offset in a test harness.

Common situations: Simulator/test harnesses that replay a commit log from scratch without clearing the Memory repo; retry logic that assumes create is idempotent; off-by-one segment-offset computation that collides with an existing segment.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20). Data as JSON: /api/errors/f125d4d9c5db1782. Report an issue: GitHub.