iced-rs/iced · error

Read primitive storage

Error message

Read primitive storage

What it means

On the render side of present(), iced takes the read lock on primitive::Storage to issue draw calls for prepared primitives. RwLock::read returns Err only on poisoning, so this panic means a writer panicked earlier while holding the lock (commonly a failing Primitive::prepare on this or another window sharing the Engine).

Source

Thrown at wgpu/src/lib.rs:515

                                load: wgpu::LoadOp::Load,
                                store: wgpu::StoreOp::Store,
                            },
                        })],
                        depth_stencil_attachment: None,
                        timestamp_writes: None,
                        occlusion_query_set: None,
                        multiview_mask: None,
                    }));
            }

            if !layer.primitives.is_empty() {
                let render_span = debug::render(debug::Primitive::Shader);

                let primitive_storage = self
                    .engine
                    .primitive_storage
                    .read()
                    .expect("Read primitive storage");

                let mut need_render = Vec::new();

                for instance in &layer.primitives {
                    let bounds = instance.bounds * scale;

                    if let Some(clip_bounds) = (instance.bounds * scale)
                        .intersection(&physical_bounds)
                        .and_then(Rectangle::snap)
                    {
                        render_pass.set_viewport(
                            bounds.x,
                            bounds.y,
                            bounds.width,
                            bounds.height,
                            0.0,
                            1.0,
                        );

View on GitHub (pinned to a8ff2d5225)

Solutions

  1. Fix the first panic in the log (the poisoner); later lock errors disappear with it
  2. Keep prepare()/draw() code of custom primitives panic-free
  3. Recover instead of crashing by using unwrap_or_else(|p| p.into_inner()) in a patched iced build

Example fix

// before (iced internals)
let primitive_storage = self.engine.primitive_storage.read().expect("Read primitive storage");

// after
let primitive_storage = self
    .engine
    .primitive_storage
    .read()
    .unwrap_or_else(|poisoned| poisoned.into_inner());
Defensive patterns

Strategy: fallback

Try / catch

// Recovery pattern for a poisoned read lock (your own RwLocks or patched iced):
let storage = match engine.primitive_storage.read() {
    Ok(guard) => guard,
    Err(poisoned) => poisoned.into_inner(), // data may be stale but consistent enough to draw
};

Prevention

When it happens

Trigger: Any frame following a panic that unwound through primitive_storage.write() in the prepare phase; multi-window applications where window A's prepare panicked and window B's render is the first subsequent lock acquisition.

Common situations: Crash cascades in multi-window desktop apps; embedding a iced viewport inside a host that catches the first panic and keeps running; devtools windows sharing the engine with the main window.

Related errors


AI-assisted analysis of iced-rs/iced@a8ff2d5225 (2026-08-17). Data as JSON: /api/errors/95cc37518d27e414. Report an issue: GitHub.