clockworklabs/SpacetimeDB · error · io::Error

AlreadyExists

AlreadyExists

Error message

segment {offset} already exists

What it means

In-memory repo counterpart of the filesystem AlreadyExists error: create_segment found an entry for this offset in the repo's segment map and it is non-empty (empty crash-remnant segments are reused). The guard prevents one Commitlog from silently clobbering another's in-memory data.

Source

Thrown at crates/commitlog/src/repo/mem.rs:66

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

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

    fn create_segment(&self, offset: u64, header: crate::segment::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_writer(&self, offset: u64) -> io::Result<Self::SegmentWriter> {
        let inner = self.segments.read().unwrap();
        let Some(buf) = inner.get(&offset) else {

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Construct a fresh Memory::new(...) for each Commitlog instance or test
  2. Remove the existing segment (remove_segment) before re-creating it
  3. Reuse the existing segment via open_segment_writer / resume instead of creating a new one

Example fix

// before
let repo = Memory::new(1 << 20);
let a = Commitlog::open_with_repo(repo.clone(), opts.clone())?;
let b = Commitlog::open_with_repo(repo, opts)?; // same offsets -> AlreadyExists

// after
let b = Commitlog::open_with_repo(Memory::new(1 << 20), opts)?; // fresh space
Defensive patterns

Strategy: validation

Validate before calling

// give each Commitlog its own memory space
fn fresh_repo(budget: u64) -> Memory {
    Memory::new(budget) // never share one Memory across instances
}

Type guard

fn is_mem_segment_exists(e: &io::Error) -> bool {
    e.kind() == io::ErrorKind::AlreadyExists
        && e.to_string().contains("segment")
        && e.to_string().contains("already exists")
}

Try / catch

let segment = match repo.create_segment(offset, header) {
    Ok(s) => s,
    Err(e) if is_mem_segment_exists(&e) => repo.open_segment_writer(offset)?, // reuse existing
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Sharing a single repo::Memory instance across two Commitlog constructions; calling create_segment for an offset whose in-memory segment already holds a header or commits; test harnesses that reuse Memory state between cases.

Common situations: Unit tests reusing one Memory repo; simulating double-initialization; porting FS-repo code that relied on fresh directories.

Related errors


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