quickwit-oss/tantivy · error
Failed to acquire write lock on SegmentManager.
Error message
Failed to acquire write lock on SegmentManager.
What it means
SegmentManager::write (src/indexer/segment_manager.rs:113) unwraps a RwLock write guard and panics if the lock is poisoned. It is used by the mutating paths: remove_empty_segments, remove_all_segments, commit, add_segment and end_merge. Since these are the only code paths holding the lock, poisoning implies one of them panicked previously — hence this panic is always secondary to an earlier failure.
Source
Thrown at src/indexer/segment_manager.rs:113
let registers_lock = self.read();
let mut segment_entries = registers_lock.uncommitted.segment_entries();
segment_entries.extend(registers_lock.committed.segment_entries());
segment_entries
}
// Lock poisoning should never happen :
// The lock is acquired and released within this class,
// and the operations cannot panic.
fn read(&self) -> RwLockReadGuard<'_, SegmentRegisters> {
self.registers
.read()
.expect("Failed to acquire read lock on SegmentManager.")
}
fn write(&self) -> RwLockWriteGuard<'_, SegmentRegisters> {
self.registers
.write()
.expect("Failed to acquire write lock on SegmentManager.")
}
/// Deletes all empty segments
fn remove_empty_segments(&self) {
let mut registers_lock = self.write();
registers_lock
.committed
.segment_entries()
.iter()
.filter(|segment| segment.meta().num_docs() == 0)
.for_each(|segment| {
registers_lock
.committed
.remove_segment(&segment.segment_id())
});
}
pub(crate) fn remove_all_segments(&self) {View on GitHub (pinned to b5d8deb80c)
Solutions
- Fix the first panic that occurred while the segment registers were locked; this panic is only the symptom.
- Serialize commits (or respect the single-writer contract) so failing commits cannot leave other threads operating on a poisoned lock.
- After a panic, drop the writer and reopen the index instead of continuing.
- Convert panic-prone custom callbacks (DocMapper, custom scorers) to return errors.
- Upgrade tantivy if the triggering panic originates inside the library.
Example fix
// before
thread::spawn(|| writer.commit()); // may panic -> poison
writer.commit(); // later panics: "Failed to acquire write lock..."
// after
let res = std::panic::catch_unwind(|| writer.commit());
if res.is_err() { writer = reopen_writer(); } else { writer = res.unwrap(); } Defensive patterns
Strategy: try-catch
Try / catch
let res = std::panic::catch_unwind(AssertUnwindSafe(|| writer.commit()));
if res.is_err() { writer = Index::open_in_dir(&dir)?; } Prevention
- Respect the single-writer contract; serialize commits
- Convert panicking callbacks to Results
- Discard the writer after any panic, then reopen
- Watch for corrupted segments and rebuild the index
When it happens
Trigger: Calling writer.commit(), or any operation triggering add_segment/end_merge/remove_all_segments, after another thread already panicked inside a SegmentManager critical section.
Common situations: Concurrent commits from multiple threads where one panics; a merge task panicking on a corrupted segment while commit proceeds; services that recover from worker-thread panics but keep the same writer alive.
Related errors
- Failed to acquire read lock on SegmentManager.
- This lock should never be poisoned
- unknown compressor id {id:?}
- actual doc store version: {doc_store_version}, max_supported
- FastFieldsPlugin is a built-in; use FastFieldsPluginWriter::
AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05).
Data as JSON: /api/errors/fdbeba7246c44ff9.
Report an issue: GitHub.