{"record":{"id":"f7329ca4f0d563fe","repo":"iced-rs/iced","slug":"lock-font-name-cache","errorCode":null,"errorMessage":"lock font name cache","messagePattern":"lock font name cache","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/src/font.rs","lineNumber":124,"sourceCode":"    /// A list of all the different standalone family variants.\n    pub const VARIANTS: &[Self] = &[\n        Self::Serif,\n        Self::SansSerif,\n        Self::Cursive,\n        Self::Fantasy,\n        Self::Monospace,\n    ];\n\n    /// Creates a [`Family::Name`] from the given string.\n    ///\n    /// The name is interned in a global cache and never freed.\n    pub fn name(name: &str) -> Self {\n        use rustc_hash::FxHashSet;\n        use std::sync::{LazyLock, Mutex};\n\n        static NAMES: LazyLock<Mutex<FxHashSet<&'static str>>> = LazyLock::new(Mutex::default);\n\n        let mut names = NAMES.lock().expect(\"lock font name cache\");\n\n        let Some(name) = names.get(name) else {\n            let name: &'static str = name.to_owned().leak();\n            let _ = names.insert(name);\n\n            return Self::Name(name);\n        };\n\n        Self::Name(name)\n    }\n}\n\nimpl From<&str> for Family {\n    fn from(name: &str) -> Self {\n        Family::name(name)\n    }\n}\n","sourceCodeStart":106,"sourceCodeEnd":142,"githubUrl":"https://github.com/iced-rs/iced/blob/2cffa99b395d84fe469b44dccb56bbacd2f1a157/core/src/font.rs#L106-L142","documentation":"`font::Family::name` interns font family names forever in a global `Mutex<FxHashSet<&'static str>>`. The `.expect(\"lock font name cache\")` panics only when that mutex is poisoned, i.e. some earlier thread panicked while holding the lock inside `Family::name`. This panic is therefore always a cascade of an earlier, different panic.","triggerScenarios":"Any call to `Family::name(\"...\")` — styling text with a named family, `Compositor::list_fonts`, or font-loading flows — executed after a previous panic left the NAMES mutex poisoned. The poison source is a panic in the brief critical section (interning/allocating the leaked name) on another thread.","commonSituations":"A panicking worker thread that happened to be interning a family name while a crash reporter swallowed the original panic; heavy multithreaded font loading at startup; any catch_unwind-based harness that keeps running after a first panic and later touches fonts.","solutions":["Find and fix the ORIGINAL panic (look earlier in the logs — poisoning is always secondary)","As a maintainer, recover instead of panicking: `NAMES.lock().unwrap_or_else(PoisonError::into_inner)`","Replace the interner lock with parking_lot::Mutex, which does not poison","Resolve family names once at startup on a single thread to shrink the race window"],"exampleFix":"// before\nlet mut names = NAMES.lock().expect(\"lock font name cache\");\n// after\nlet mut names = NAMES\n    .lock()\n    .unwrap_or_else(std::sync::PoisonError::into_inner);","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// maintainer-side: never let interning panic on poisoned state\nlet mut names = NAMES\n    .lock()\n    .unwrap_or_else(std::sync::PoisonError::into_inner); // data is still valid","preventionTips":["Fix the FIRST panic in a process — lock poisoning always has an earlier root cause","Resolve font family names once at startup on one thread instead of lazily from many threads","If you wrap iced in catch_unwind crash reporting, exit or rebuild state after a panic instead of continuing","Prefer locking primitives without poisoning (parking_lot) in your own globals that mirror this pattern"],"tags":["rust","iced","font","mutex","lock-poisoning","panic"],"backgroundTag":"lock-poisoned","analyzedSha":"2cffa99b395d84fe469b44dccb56bbacd2f1a157","analyzedAt":"2026-08-16T19:41:49.083Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}