clockworklabs/SpacetimeDB · error · io::Error
no space left on device
Error message
no space left on device
What it means
The in-memory commitlog models a device with a finite space budget: a shared u64 counter ('space') is decremented as segments grow their allocation. When Segment::write needs more pages than remain in the budget, it returns ErrorKind::StorageFull via enospc() with the classic ENOSPC message 'no space left on device'. This lets simulation tests exercise disk-full behavior deterministically (the test fixture at line ~470 constructs the repo with u64::MAX to disable the quota).
Source
Thrown at crates/dst/src/sim/commitlog.rs:463
self.inner.seek(pos)
}
}
impl SegmentLen for ReadOnlySegment {}
fn next_page_multiple(size: u64) -> io::Result<u64> {
let page = PAGE_SIZE as u64;
let remainder = size % page;
if remainder == 0 {
return Ok(size);
}
size.checked_add(page - remainder)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "allocation size overflow"))
}
fn enospc() -> io::Error {
io::Error::new(io::ErrorKind::StorageFull, "no space left on device")
}
#[cfg(test)]
mod tests {
use std::io::{Read, Seek, Write};
use super::*;
fn segment() -> Segment {
Segment::from_shared(Arc::new(Mutex::new(u64::MAX)), Arc::new(RwLock::new(Storage::new())))
}
#[test]
fn write_overwrites_at_seek_position() {
let mut segment = segment();
segment.write_all(b"abcdef").unwrap();
segment.seek(io::SeekFrom::Start(2)).unwrap();View on GitHub (pinned to 6dee26c6ef)
Solutions
- Construct Memory with a larger space budget (u64::MAX disables the quota entirely) when disk-full is not the behavior under test
- Remove/rotate old segments (remove_segment frees their budget share) before writing new ones
- If testing the ENOSPC path intentionally, size the budget just below the write that should fail, and assert on ErrorKind::StorageFull
Example fix
// before let repo = Memory::new(/* space */ 4096); // ... writes beyond 4 KiB -> StorageFull // after let repo = Memory::new(u64::MAX); // quota disabled for this test // or: remove_segment(old_offset)?; before appending
Defensive patterns
Strategy: fallback
Validate before calling
// If you expose the budget, pre-check headroom before a big append:
fn headroom(space: &Mutex<u64>, allocated: u64, want: u64) -> bool {
*space.lock().unwrap() >= want.saturating_sub(allocated).next_multiple_of(4096)
} Try / catch
match seg.write(buf) {
Err(ref e) if e.kind() == std::io::ErrorKind::StorageFull => {
repo.remove_segment(oldest_retired_offset)?; // free budget
seg.write(buf)? // retry after freeing
}
r => r?,
} Prevention
- Size the Memory repo's space budget generously vs the data the test writes; u64::MAX disables the quota
- Remove/rotate retired segments so the budget is reclaimed before appending
- Assert on ErrorKind::StorageFull (not the message) when testing the full-disk path
When it happens
Trigger: Creating a Memory repo with a finite space budget and writing more bytes than budgeted across segments; growing several segments concurrently so the shared counter is exhausted; tests that deliberately size the budget to trigger the full-disk path.
Common situations: Simulation harnesses for the commitlog/durable-log layer: quota set smaller than the log the test produces; forgetting to raise the budget when a test's data volume grows; retention not removing old segments so the budget drains.
Related errors
- segment {offset} already exists
- segment {offset} does not exist
- write position overflow
- invalid seek to a negative or overflowing position
- allocation size overflow
AI-assisted analysis of clockworklabs/SpacetimeDB@6dee26c6ef (2026-08-20).
Data as JSON: /api/errors/7f610d9f54fb425b.
Report an issue: GitHub.