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

  1. Fix the first panic that occurred while the segment registers were locked; this panic is only the symptom.
  2. Serialize commits (or respect the single-writer contract) so failing commits cannot leave other threads operating on a poisoned lock.
  3. After a panic, drop the writer and reopen the index instead of continuing.
  4. Convert panic-prone custom callbacks (DocMapper, custom scorers) to return errors.
  5. 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

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


AI-assisted analysis of quickwit-oss/tantivy@b5d8deb80c (2026-09-05). Data as JSON: /api/errors/fdbeba7246c44ff9. Report an issue: GitHub.