quickwit-oss/tantivy · error

Failed to acquire write lock in delete queue

Error message

Failed to acquire write lock in delete queue

What it means

NextBlock::next_block takes a read lock to check whether the garbage-collector side of the delete queue has reached a closed block. The expect message mentions 'write lock' but the actual failure is a poisoned RwLock (read side): a previous holder panicked, so the queue cursor state is unreliable.

Source

Thrown at src/indexer/delete_queue.rs:136

    Writer(DeleteQueue),
    Closed(Arc<Block>),
}

struct NextBlock(RwLock<InnerNextBlock>);

impl From<DeleteQueue> for NextBlock {
    fn from(delete_queue: DeleteQueue) -> NextBlock {
        NextBlock(RwLock::new(InnerNextBlock::Writer(delete_queue)))
    }
}

impl NextBlock {
    fn next_block(&self) -> Option<Arc<Block>> {
        {
            let next_read_lock = self
                .0
                .read()
                .expect("Failed to acquire write lock in delete queue");
            if let InnerNextBlock::Closed(ref block) = *next_read_lock {
                return Some(Arc::clone(block));
            }
        }
        let next_block;
        {
            let mut next_write_lock = self
                .0
                .write()
                .expect("Failed to acquire write lock in delete queue");
            match *next_write_lock {
                InnerNextBlock::Closed(ref block) => {
                    return Some(Arc::clone(block));
                }
                InnerNextBlock::Writer(ref writer) => match writer.flush() {
                    Some(flushed_next_block) => {
                        next_block = flushed_next_block;
                    }

View on GitHub (pinned to b5d8deb80c)

Solutions

  1. Fix the original panic that poisoned the delete queue lock.
  2. Restart indexing / recreate the IndexWriter and its GC worker.
  3. Harden flush/push paths against panics (no unwraps on user data).
  4. Isolate GC in its own thread with catch_unwind so poisoning does not propagate.

Example fix

// before
block_garbage_collector.next_block(); // panics after unrelated panic
// after
let next = std::panic::catch_unwind(|| gc.next_block());
if next.is_err() { restart_gc_worker(&queue); }
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

let next = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| gc.next_block()));
if next.is_err() { restart_delete_gc(&index)?; }

Prevention

When it happens

Trigger: The delete-GC worker calls next_block() while another thread has panicked holding the queue lock (e.g. in flush() or push()).

Common situations: Background garbage collection of delete blocks running alongside a panicking indexing thread; usually a secondary symptom of an earlier crash.

Related errors


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