{"record":{"id":"2f8509bca3dce21a","repo":"quickwit-oss/tantivy","slug":"managed-directory-rlock-poisoned-in-garbage-collec","errorCode":null,"errorMessage":"Managed directory rlock poisoned in garbage collect.","messagePattern":"Managed directory rlock poisoned in garbage collect\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/directory/managed_directory.rs","lineNumber":130,"sourceCode":"    ) -> crate::Result<GarbageCollectionResult> {\n        debug!(\"Garbage collect\");\n        let mut files_to_delete = vec![];\n\n        // It is crucial to get the living files after acquiring the\n        // read lock of meta information. That way, we\n        // avoid the following scenario.\n        //\n        // 1) we get the list of living files.\n        // 2) someone creates a new file.\n        // 3) we start garbage collection and remove this file\n        // even though it is a living file.\n        //\n        // releasing the lock as .delete() will use it too.\n        {\n            let meta_informations_rlock = self\n                .meta_informations\n                .read()\n                .expect(\"Managed directory rlock poisoned in garbage collect.\");\n\n            // The point of this second \"file\" lock is to enforce the following scenario\n            // 1) process B tries to load a new set of searcher.\n            // The list of segments is loaded\n            // 2) writer change meta.json (for instance after a merge or a commit)\n            // 3) gc kicks in.\n            // 4) gc removes a file that was useful for process B, before process B opened it.\n            match self.acquire_lock(&META_LOCK) {\n                Ok(_meta_lock) => {\n                    let living_files = get_living_files();\n                    for managed_path in &meta_informations_rlock.managed_paths {\n                        if !living_files.contains(managed_path) {\n                            files_to_delete.push(managed_path.clone());\n                        }\n                    }\n                }\n                Err(err) => {\n                    error!(\"Failed to acquire lock for GC\");","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/quickwit-oss/tantivy/blob/b5d8deb80c26924e6b007a5b1a7630f35ca64de4/src/directory/managed_directory.rs#L112-L148","documentation":"Fires in ManagedDirectory::garbage_collect when acquiring the read lock on meta_informations fails because the RwLock is poisoned — i.e. another thread panicked while holding the lock, leaving the managed-file set in an unknown state. It is a generic poisoned-lock sentinel (.expect on .read()), not a validation of file inputs; garbage collection cannot safely decide which files are deletable, so the panic aborts the GC run.","triggerScenarios":"Thrown at src/directory/managed_directory.rs:130 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Find and fix the original panic that poisoned the meta_informations lock (inspect earlier panic messages/logs)","Keep the GC critical sections free of panicking code (e.g. map IO errors instead of unwrapping)","Consider using lock recovery (into_inner / PoisonError handling) if tearing down the directory is preferable"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}