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
- Construct a fresh Memory::new(...) for each Commitlog instance or test
- Remove the existing segment (remove_segment) before re-creating it
- 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
- Construct a new Memory::new(...) per Commitlog and per test
- In test harnesses, build fresh repos in setup rather than resetting shared ones
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.