iced-rs/iced · error

Write font system

Error message

Write font system

What it means

The CPU text-outline path (stroked text in iced's geometry/canvas API) takes the global font-system write lock to shape glyph outlines via the swash cache. `.expect("Write font system")` panics if that lock is poisoned; calling it re-entrantly while the same thread already holds a font-system guard would deadlock instead.

Source

Thrown at graphics/src/geometry/text.rs:84

        let translation_x = match self.align_x {
            Alignment::Default | Alignment::Left | Alignment::Justified => self.position.x,
            Alignment::Center => self.position.x - paragraph.min_width() / 2.0,
            Alignment::Right => self.position.x - paragraph.min_width(),
        };

        let translation_y = {
            match self.align_y {
                alignment::Vertical::Top => self.position.y,
                alignment::Vertical::Center => self.position.y - paragraph.min_height() / 2.0,
                alignment::Vertical::Bottom => self.position.y - paragraph.min_height(),
            }
        };

        let buffer = paragraph.buffer();
        let mut swash_cache = cosmic_text::SwashCache::new();

        let mut font_system = text::font_system().write().expect("Write font system");

        for run in buffer.layout_runs() {
            for glyph in run.glyphs.iter() {
                let physical_glyph = glyph.physical((0.0, 0.0), 1.0);

                let start_x = translation_x + glyph.x + glyph.x_offset;
                let start_y = translation_y + glyph.y_offset + run.line_y;
                let offset = Vector::new(start_x, start_y);

                if let Some(commands) =
                    swash_cache.get_outline_commands(font_system.raw(), physical_glyph.cache_key)
                {
                    let glyph = Path::new(|path| {
                        use cosmic_text::Command;

                        for command in commands {
                            match command {
                                Command::MoveTo(p) => {

View on GitHub (pinned to 2cffa99b39)

Solutions

  1. Find and fix the original panic under the font-system lock — this expect is collateral
  2. Draw geometry text only on the render thread, never from worker threads
  3. Recover from poison in the library: `write().unwrap_or_else(PoisonError::into_inner)`
  4. Prefer the filled-text path when outline rendering is optional for your visuals

Example fix

// before
let mut font_system = text::font_system().write().expect("Write font system");
// after
let mut font_system = text::font_system()
    .write()
    .unwrap_or_else(std::sync::PoisonError::into_inner);
Defensive patterns

Strategy: validation

Try / catch

let outlined = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    draw_stroked_text(&frame, &paragraph);
}));
if outlined.is_err() {
    draw_filled_text(&frame, &paragraph); // fall back to the filled-text path
}

Prevention

When it happens

Trigger: Drawing stroked/outlined text in a canvas frame once another thread has panicked inside a critical section of the shared font system; outlining text from inside a callback that itself is running under a font-system lock.

Common situations: Canvas-heavy apps mixing stroke text with filled text after any swallowed panic in the text pipeline; debug rendering overlays that outline glyphs on multiple threads.

Related errors


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