iced-rs/iced · error
primitive storage should be writable
Error message
primitive storage should be writable
What it means
The wgpu Engine stores primitive::Storage behind an Arc<RwLock> shared by every window compositor. std's RwLock::write returns Err only when the lock is poisoned, i.e. some thread panicked while holding it; this expect in Engine::trim then reports the poisoning, not the original fault. The real panic happened earlier, typically inside a custom primitive's prepare or another window's render pass sharing the same engine.
Source
Thrown at wgpu/src/engine.rs:66
device,
queue,
_shell: shell,
}
}
#[cfg(any(feature = "image", feature = "svg"))]
pub fn create_image_cache(&self) -> crate::image::Cache {
self.image_pipeline
.create_cache(&self.device, &self.queue, &self._shell)
}
pub fn trim(&mut self) {
self.text_pipeline.trim();
self.primitive_storage
.write()
.expect("primitive storage should be writable")
.trim();
}
}
View on GitHub (pinned to 3de451447b)
Solutions
- Search the log upwards for the FIRST panic: it poisoned the lock and is the actual bug to fix
- Make custom Primitive::prepare implementations panic-free (no indexing, unwrap, or division by zero on user data)
- Isolate risky prepare code with std::panic::catch_unwind so it cannot poison the shared lock
- As a last resort, recover the data with unwrap_or_else(|poisoned| poisoned.into_inner()) in a patched iced
Example fix
// before (iced internals)
self.primitive_storage.write().expect("primitive storage should be writable").trim();
// after
self.primitive_storage
.write()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.trim(); Defensive patterns
Strategy: fallback
Try / catch
// Recovery pattern when a poisoned shared lock must not kill the process
// (applies to your own RwLocks, or a patched iced):
let mut storage = match engine.primitive_storage.write() {
Ok(guard) => guard,
Err(poisoned) => {
log::error!("primitive storage poisoned; recovering");
poisoned.into_inner()
}
}; Prevention
- The original panic is the bug: always investigate the first crash in the log, not this one
- Never panic while holding locks: keep custom Primitive::prepare implementations total
- In multi-window apps, isolate untrusted drawing code so one window cannot poison the shared Engine
When it happens
Trigger: A multi-window iced_wgpu application where a panic during another window's present() unwinds through the primitive_storage write guard, and a later trim() (widget-cache trimming) hits the poisoned lock; user code that swallowed the first panic with catch_unwind, leaving the lock poisoned.
Common situations: Desktop apps with several windows or an embedded viewport plus devtools; custom shader primitive implementations that index or unwrap user data; the first panic being logged above this one in the crash log.
Related errors
AI-assisted analysis of iced-rs/iced@3de451447b (2026-08-17).
Data as JSON: /api/errors/b135b3ee4944b2bd.
Report an issue: GitHub.