{"record":{"id":"aa96cfb1e9854fae","repo":"quickwit-oss/tantivy","slug":"this-lock-should-never-be-poisoned","errorCode":null,"errorMessage":"This lock should never be poisoned","messagePattern":"This lock should never be poisoned","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/indexer/index_writer_status.rs","lineNumber":26,"sourceCode":"#[derive(Clone)]\npub(crate) struct IndexWriterStatus<D: Document = TantivyDocument> {\n    inner: Arc<Inner<D>>,\n}\n\nimpl<D: Document> IndexWriterStatus<D> {\n    /// Returns true iff the index writer is alive.\n    pub fn is_alive(&self) -> bool {\n        self.inner.as_ref().is_alive()\n    }\n\n    /// Returns a copy of the operation receiver.\n    /// If the index writer was killed, returns `None`.\n    pub fn operation_receiver(&self) -> Option<AddBatchReceiver<D>> {\n        let rlock = self\n            .inner\n            .receive_channel\n            .read()\n            .expect(\"This lock should never be poisoned\");\n        rlock.as_ref().cloned()\n    }\n\n    /// Create an index writer bomb.\n    /// If dropped, the index writer status will be killed.\n    pub(crate) fn create_bomb(&self) -> IndexWriterBomb<D> {\n        IndexWriterBomb {\n            inner: Some(self.inner.clone()),\n        }\n    }\n}\n\nstruct Inner<D: Document> {\n    is_alive: AtomicBool,\n    receive_channel: RwLock<Option<AddBatchReceiver<D>>>,\n}\n\nimpl<D: Document> Inner<D> {","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/indexer/index_writer_status.rs#L8-L44","documentation":"This panic comes from unwrapping a std::sync::RwLock read guard in IndexWriterStatus::operation_receiver (src/indexer/index_writer_status.rs:26). It fires only if the mutex is poisoned, i.e. a thread panicked while holding the lock. The code asserts this can never happen because the guard body never panics: it only clones an Option<AddBatchReceiver>.","triggerScenarios":"Calling IndexWriterStatus::operation_receiver() after another thread panicked while holding the receive_channel RwLock write guard (e.g. inside kill(), which is invoked by a dropped IndexWriterBomb). In practice this requires a genuine bug elsewhere that panics between lock acquisition and release.","commonSituations":"A user creates an IndexWriterBomb and drops it in a thread that also panics nearby; mixing panics with multi-threaded index writer access; catching panics with catch_unwind and continuing to use the writer afterwards, leaving a poisoned lock behind.","solutions":["Fix the underlying panic that poisoned the lock — inspect the panic message that preceded this one; this panic is always secondary.","Avoid catching panics and reusing the same IndexWriter / IndexWriterStatus across the panic boundary; drop and recreate the writer instead.","If you intentionally use bombs, ensure the bomb-drop path (kill) cannot panic, and that no user code runs while the lock is held.","Update tantivy — if you can reproduce poisoning without an obvious source panic, report it; the invariant may be violated by a library bug."],"exampleFix":"// before: reuse after caught panic\nlet res = std::panic::catch_unwind(|| writer_operation());\n// ... continue using same writer.status.operation_receiver()\n\n// after: rebuild the writer if a panic occurred\nlet res = std::panic::catch_unwind(|| writer_operation());\nif res.is_err() { writer = recreate_writer(); }","handlingStrategy":"try-catch","validationCode":"// best effort: only access the writer from one logical owner\n// let recv = writer.status.operation_receiver(); // only while no bomb may drop concurrently","typeGuard":null,"tryCatchPattern":"// Rust panics cannot be caught with try/catch; isolate the whole writer scope\nlet result = std::panic::catch_unwind(AssertUnwindSafe(|| {\n    use_index_writer(&writer)\n}));\nif result.is_err() { writer = reopen_writer(); }","preventionTips":["Never catch_unwind and then keep using the same IndexWriter/IndexWriterStatus","Do not run arbitrary fallible user code while holding internal writer locks","Keep bomb-drop paths panic-free","Recreate the writer after any panic involving it"],"tags":["rust","panic","mutex-poisoned","concurrency","indexing"],"backgroundTag":"mutex-poisoned","analyzedSha":"b5d8deb80c26924e6b007a5b1a7630f35ca64de4","analyzedAt":"2026-09-05T13:20:51.521Z","contentChangedAt":"2026-09-05T13:20:51.521Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}