rustfs/rustfs · error · AuditError
Storage not available: {0}
Error message
Storage not available: {0} What it means
Index::find() (crates/rio/src/compress_index.rs:150) needs at least one index entry to map an offset to a block. If index.info is empty — the index was constructed but add() was never called (or the stream was too small to record any entry, since entries closer than MIN_INDEX_DIST are skipped) — find() returns UnexpectedEof 'empty index' regardless of the offset being in bounds.
Source
Thrown at crates/audit/src/error.rs:41
#[error("Configuration error: {0}")]
Configuration(String, #[source] Option<Box<dyn std::error::Error + Send + Sync>>),
#[error("config not loaded")]
ConfigNotLoaded,
#[error("Target error: {0}")]
Target(#[from] rustfs_targets::TargetError),
#[error("System not initialized: {0}")]
NotInitialized(String),
#[error("System already initialized")]
AlreadyInitialized,
#[error("Audit system is paused; entry was not accepted")]
Paused,
#[error("Storage not available: {0}")]
StorageNotAvailable(String),
#[error("Failed to save configuration: {0}")]
SaveConfig(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("Failed to load configuration: {0}")]
LoadConfig(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("Serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Join error: {0}")]
Join(#[from] tokio::task::JoinError),
}
View on GitHub (pinned to 35af688cd9)
Solutions
- Skip seek-index lookups when index.is_empty() and fall back to a linear decode from stream start.
- Ensure load()/load_stream() ran successfully (and its byte range actually contained the index chunk) before find().
- For small objects, store/stream without an index or synthesize a single (0,0) entry.
- Guard with is_empty() in callers: if index.is_empty() { decode_from_start() } else { index.find(off)? }.
Example fix
// before
let (c, u) = index.find(offset)?; // panics path if index empty
// after: fall back to linear read
let (c, u) = if index.is_empty() {
(0i64, 0i64)
} else {
index.find(offset)?
}; Defensive patterns
Strategy: fallback
Validate before calling
if index.is_empty() {
// no seek index for this object: decode linearly from the start
return decode_from_start(&stream);
}
let (c, u) = index.find(offset)?; Type guard
fn index_has_entries(index: &Index) -> bool {
!index.is_empty()
} Try / catch
match index.find(offset) {
Ok(pos) => pos,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof && e.to_string().contains("empty index") => {
decode_from_start(&stream) // fallback: no entries recorded
}
Err(e) => return Err(e),
} Prevention
- Check index.is_empty() before find() and keep a linear-decode fallback.
- Ensure load()/load_stream() completed and the byte range actually contained the chunk.
- Tiny streams may legitimately have no entries — design the read path for that.
When it happens
Trigger: Calling find() on a fresh Index::new(); loading an index chunk that legitimately contains zero entries (tiny streams where every add was suppressed by the MIN_INDEX_DIST rule); forgetting to call load()/load_stream() before find().
Common situations: Small-object fast path skipping index building but still calling find(); deserialization succeeded but read the wrong byte range; race where a reader seeks before the writer finished the first index entry.
Related errors
- Failed to save configuration: {0}
- Failed to load configuration: {0}
- Serialization error: {0}
- I/O error: {0}
- Target error: {0}
AI-assisted analysis of rustfs/rustfs@35af688cd9 (2026-08-20).
Data as JSON: /api/errors/f6ed9b6fdc972459.
Report an issue: GitHub.