GraphiteEditor/Graphite · error
UUID mutex poisoned
Error message
UUID mutex poisoned
What it means
generate_uuid() in core-types guards a process-global static RNG (Mutex<Option<ChaCha20Rng>>) and panics with "UUID mutex poisoned" when lock() returns Err. A Rust Mutex is poisoned only after some thread panicked while holding it, so this message is always a secondary failure: the real bug is the earlier panic that left the lock poisoned. Once poisoned, every later NodeId::new() and generate_uuid() call in the entire process panics immediately.
Source
Thrown at node-graph/libraries/core-types/src/uuid.rs:66
static RNG: Mutex<Option<ChaCha20Rng>> = Mutex::new(None);
thread_local! {
pub static UUID_SEED: Cell<Option<u64>> = const { Cell::new(None) };
}
pub fn set_uuid_seed(random_seed: u64) {
UUID_SEED.with(|seed| seed.set(Some(random_seed)))
}
pub fn generate_uuid() -> u64 {
let Ok(mut lock) = RNG.lock() else { panic!("UUID mutex poisoned") };
if lock.is_none() {
UUID_SEED.with(|seed| {
let random_seed = seed.get().unwrap_or(42);
*lock = Some(ChaCha20Rng::seed_from_u64(random_seed));
})
}
lock.as_mut().map(ChaCha20Rng::next_u64).expect("UUID mutex poisoned")
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, graphene_hash::CacheHash, PartialOrd, Ord, DynAny)]
#[cfg_attr(feature = "wasm", derive(tsify::Tsify), tsify(large_number_types_as_bigints))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeId(pub u64);
impl NodeId {
pub fn new() -> Self {
Self(generate_uuid())
}
}
impl std::fmt::Display for NodeId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)View on GitHub (pinned to c507b35645)
Solutions
- Treat this as a symptom: find the FIRST panic in the logs (it precedes the poisoning) and fix that root cause
- Restart the process or test worker to clear the poisoned mutex; the global RNG state cannot be repaired from user code
- Make generate_uuid() poison-tolerant: the RNG has no cross-call invariants, so recover the guard with RNG.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
- Longer term, replace the global Mutex<Option<Rng>> with a lock-free atomic counter or per-thread RNGs so poisoning is structurally impossible
Example fix
// before (uuid.rs)
let Ok(mut lock) = RNG.lock() else { panic!("UUID mutex poisoned") };
// after: RNG state carries no cross-call invariants, so recovering from poisoning is safe
let mut lock = RNG.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); Defensive patterns
Strategy: try-catch
Try / catch
let ids = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
(0..count).map(|_| NodeId::new()).collect::<Vec<_>>()
}));
if ids.is_err() {
// mutex poisoned (or another ID panic): locate the ORIGINAL panic in logs, then restart this worker/process
} Prevention
- Always log the first panic with a backtrace; a poisoned-mutex report later is only a symptom
- Run graph evaluation on worker threads that can be torn down and recreated after a panic instead of reusing the poisoned process
- Avoid code that can panic while holding global locks; prefer Result-based error handling in those critical sections
When it happens
Trigger: Any earlier panic on any thread while the RNG mutex guard is alive (the critical section in generate_uuid) poisons the lock; every subsequent call, e.g. NodeId::new() during node creation, document load, or paste, then panics with this message instead of producing an ID.
Common situations: A graph-evaluation worker thread hits a different .expect panic (GPU error, encoding error) and the process keeps running, after which every ID allocation dies; parallel test runs where one failing test poisons globals and cascading failures report "poisoned" instead of the root cause; hosts that use catch_unwind to survive panics but keep reusing the same process.
Related errors
- Failed to lock internal overlay context
- Ungrouped folder must have a parent
- Solidify Stroke node should exist
- In `check_layer()`: there should be a `target`
- Artboard should have a primary input
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/4ec522d9eb3147a4.
Report an issue: GitHub.