{"record":{"id":"fdbeba7246c44ff9","repo":"quickwit-oss/tantivy","slug":"failed-to-acquire-write-lock-on-segmentmanager","errorCode":null,"errorMessage":"Failed to acquire write lock on SegmentManager.","messagePattern":"Failed to acquire write lock on SegmentManager\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/indexer/segment_manager.rs","lineNumber":113,"sourceCode":"        let registers_lock = self.read();\n        let mut segment_entries = registers_lock.uncommitted.segment_entries();\n        segment_entries.extend(registers_lock.committed.segment_entries());\n        segment_entries\n    }\n\n    // Lock poisoning should never happen :\n    // The lock is acquired and released within this class,\n    // and the operations cannot panic.\n    fn read(&self) -> RwLockReadGuard<'_, SegmentRegisters> {\n        self.registers\n            .read()\n            .expect(\"Failed to acquire read lock on SegmentManager.\")\n    }\n\n    fn write(&self) -> RwLockWriteGuard<'_, SegmentRegisters> {\n        self.registers\n            .write()\n            .expect(\"Failed to acquire write lock on SegmentManager.\")\n    }\n\n    /// Deletes all empty segments\n    fn remove_empty_segments(&self) {\n        let mut registers_lock = self.write();\n        registers_lock\n            .committed\n            .segment_entries()\n            .iter()\n            .filter(|segment| segment.meta().num_docs() == 0)\n            .for_each(|segment| {\n                registers_lock\n                    .committed\n                    .remove_segment(&segment.segment_id())\n            });\n    }\n\n    pub(crate) fn remove_all_segments(&self) {","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/indexer/segment_manager.rs#L95-L131","documentation":"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.","triggerScenarios":"Calling writer.commit(), or any operation triggering add_segment/end_merge/remove_all_segments, after another thread already panicked inside a SegmentManager critical section.","commonSituations":"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.","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."],"exampleFix":"// before\nthread::spawn(|| writer.commit()); // may panic -> poison\nwriter.commit(); // later panics: \"Failed to acquire write lock...\"\n\n// after\nlet res = std::panic::catch_unwind(|| writer.commit());\nif res.is_err() { writer = reopen_writer(); } else { writer = res.unwrap(); }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"let res = std::panic::catch_unwind(AssertUnwindSafe(|| writer.commit()));\nif res.is_err() { writer = Index::open_in_dir(&dir)?; }","preventionTips":["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"],"tags":["rust","panic","mutex-poisoned","segment-management","commit"],"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"}