iced-rs/iced · error

lock font name cache

Error message

lock font name cache

What it means

`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.

Source

Thrown at core/src/font.rs:124

    /// A list of all the different standalone family variants.
    pub const VARIANTS: &[Self] = &[
        Self::Serif,
        Self::SansSerif,
        Self::Cursive,
        Self::Fantasy,
        Self::Monospace,
    ];

    /// Creates a [`Family::Name`] from the given string.
    ///
    /// The name is interned in a global cache and never freed.
    pub fn name(name: &str) -> Self {
        use rustc_hash::FxHashSet;
        use std::sync::{LazyLock, Mutex};

        static NAMES: LazyLock<Mutex<FxHashSet<&'static str>>> = LazyLock::new(Mutex::default);

        let mut names = NAMES.lock().expect("lock font name cache");

        let Some(name) = names.get(name) else {
            let name: &'static str = name.to_owned().leak();
            let _ = names.insert(name);

            return Self::Name(name);
        };

        Self::Name(name)
    }
}

impl From<&str> for Family {
    fn from(name: &str) -> Self {
        Family::name(name)
    }
}

View on GitHub (pinned to 2cffa99b39)

Solutions

  1. Find and fix the ORIGINAL panic (look earlier in the logs — poisoning is always secondary)
  2. As a maintainer, recover instead of panicking: `NAMES.lock().unwrap_or_else(PoisonError::into_inner)`
  3. Replace the interner lock with parking_lot::Mutex, which does not poison
  4. Resolve family names once at startup on a single thread to shrink the race window

Example fix

// before
let mut names = NAMES.lock().expect("lock font name cache");
// after
let mut names = NAMES
    .lock()
    .unwrap_or_else(std::sync::PoisonError::into_inner);
Defensive patterns

Strategy: fallback

Try / catch

// maintainer-side: never let interning panic on poisoned state
let mut names = NAMES
    .lock()
    .unwrap_or_else(std::sync::PoisonError::into_inner); // data is still valid

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of iced-rs/iced@2cffa99b39 (2026-08-16). Data as JSON: /api/errors/f7329ca4f0d563fe. Report an issue: GitHub.