{"record":{"id":"7c52edd660c06e4a","repo":"quickwit-oss/tantivy","slug":"failed-to-acquire-read-lock-on-segmentmanager","errorCode":null,"errorMessage":"Failed to acquire read lock on SegmentManager.","messagePattern":"Failed to acquire read lock on SegmentManager\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/indexer/segment_manager.rs","lineNumber":107,"sourceCode":"                .uncommitted\n                .get_mergeable_segments(in_merge_segment_ids),\n        )\n    }\n    /// Returns all of the segment entries (committed or uncommitted)\n    pub fn segment_entries(&self) -> Vec<SegmentEntry> {\n        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","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/indexer/segment_manager.rs#L89-L125","documentation":"SegmentManager::read (src/indexer/segment_manager.rs:107) unwraps a RwLock read guard on the segment registers and panics with this message if the lock is poisoned. The comment above it states the lock is acquired and released only inside SegmentManager and its operations are designed not to panic, so poisoning indicates an internal invariant violation — usually following another panic during segment operations (commit, merge, garbage collection).","triggerScenarios":"Any read-path call — fmt/debug printing, get_mergeable_segments, segment_entries, start_merge, committed_segment_metas — after a thread panicked while holding the SegmentManager write lock (e.g. during commit, add_segment or end_merge).","commonSituations":"An indexing/commit thread panics (e.g. on a schema mismatch or corrupted segment) while other reader threads continue using the same IndexReader/writer; long-lived services that swallow panics in worker threads; bugs in custom DocMappers or custom scorers that panic during commit.","solutions":["Fix the root-cause panic that happened while the write lock was held; the first panic in your logs is the real error.","After a panic, discard the Index and reopen it from disk rather than continuing with the poisoned SegmentManager.","Wrap indexing/commit code so a panic tears down the whole writer+reader set, not just one thread.","Check custom DocMapping/segment-merge callbacks for panics and convert those error paths to Result instead.","Report to tantivy if reproducible without an application-side panic."],"exampleFix":"// before: keep using reader after worker panic\nstd::panic::catch_unwind(|| writer.commit()).ok();\nlet segs = reader.searcher().segment_readers(); // may hit poisoned lock\n\n// after: reopen on panic\nif std::panic::catch_unwind(|| writer.commit()).is_err() {\n    (writer, reader) = Index::open_in_dir(&dir)?; // fresh SegmentManager\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match std::panic::catch_unwind(AssertUnwindSafe(|| writer.commit())) {\n    Ok(r) => r?,\n    Err(_) => { (writer, reader) = reopen_index()?; }\n}","preventionTips":["Fix the first panic in your logs — it is the root cause","Tear down writer+reader together after a panic","Avoid panicking custom DocMapper/merge callbacks; return Result","Upgrade tantivy for known internal bugs; keep segments uncorrupted"],"tags":["rust","panic","mutex-poisoned","segment-management","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"}