quickwit-oss/tantivy · error
Mmap cache lock is poisoned.
Error message
Mmap cache lock is poisoned.
What it means
This panic occurs when the RwLock guarding the segment mmap cache is poisoned, i.e. another thread panicked while holding the read or write lock on that cache. Tantivy uses expect() here because lock poisoning on an internal cache means a prior thread crashed mid-operation, leaving the cache in an indeterminate state.
Source
Thrown at src/directory/mmap_directory/mod.rs:301
fn resolve_path(&self, relative_path: &Path) -> PathBuf {
self.inner.root_path.join(relative_path)
}
/// Returns some statistical information
/// about the Mmap cache.
///
/// The `MmapDirectory` embeds a `MmapDirectory`
/// to avoid multiplying the `mmap` system calls.
pub fn get_cache_info(&self) -> CacheInfo {
self.inner
.mmap_cache
.write()
.expect("mmap cache lock is poisoned")
.remove_weak_ref();
self.inner
.mmap_cache
.read()
.expect("Mmap cache lock is poisoned.")
.get_info()
}
}
/// We rely on fs2 for file locking. On Windows & MacOS this
/// uses BSD locks (`flock`). The lock is actually released when
/// the `File` object is dropped and its associated file descriptor
/// is closed.
struct ReleaseLockFile {
_file: File,
path: PathBuf,
}
impl Drop for ReleaseLockFile {
fn drop(&mut self) {
debug!("Releasing lock {:?}", self.path);
}
}View on GitHub (pinned to b5d8deb80c)
Solutions
- Find and fix the original panic that poisoned the lock (scan earlier logs for the root panic/backtrace).
- Reinitialize the index reader/searcher after the panic instead of reusing the same Index across threads.
- Ensure custom Directory/wrap implementations do not panic while accessing mmap cache internals.
- If caused by memory pressure, reduce concurrent mmap usage or increase address space / mmap limits.
Example fix
// before: reusing a shared Index across threads after a panic let info = index.get_cache_info(); // panics: poisoned // after: recreate the index handle after catching the root failure let index = Index::open_in_dir(&dir)?; let info = index.get_cache_info();
Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: check index dir is readable/mmap-able before use
std::fs::read(dir.join("meta.json")).map_err(|e| format!("index unreadable: {e}"))?; Type guard
fn lock_ok<T>(guard: std::sync::LockResult<T>) -> Option<T> { guard.ok() } Try / catch
let info = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| index.get_cache_info()));
match info {
Ok(v) => use(v),
Err(_) => { let index = reopen_index(&dir)?; }
} Prevention
- Fix root panics in worker threads before they poison shared locks
- Recreate Index/Searcher handles after any caught panic
- Avoid panicking inside custom Directory or cache code
- Monitor logs for earlier panics — this error is always secondary
When it happens
Trigger: A panic happens in another thread while it holds the mmap cache lock (e.g. during cache eviction or weak-ref removal), then any thread calls get_cache_info().
Common situations: A background indexing or search thread panics (e.g. corrupt segment, OOM during mmap) while holding the cache lock; the panic message seen is the propagated poisoning, not the root cause.
Related errors
- Lock poisoned. This should never happen
- Field reader cache lock poisoned. This should never happen.
- Failed to acquire write lock in delete queue
- Failed to acquire write lock on delete queue writer
- This lock should never be poisoned
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/bf2d79c324e65107.
Report an issue: GitHub.