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
- Fix the original panic that poisoned the delete queue lock.
- Restart indexing / recreate the IndexWriter and its GC worker.
- Harden flush/push paths against panics (no unwraps on user data).
- 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
- Isolate the GC thread with panic recovery + restart
- Fix root panics in push/flush paths first
- Avoid unwraps on user-supplied data in delete operations
- Monitor for the first panic — this one is secondary
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
- Mmap cache lock is poisoned.
- Lock poisoned. This should never happen
- Field reader cache lock poisoned. This should never happen.
- 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/14c7f462504efc55.
Report an issue: GitHub.