FyroxEngine/Fyrox · error
Attempt to replace object in pool using dangling handle!…
Error message
Attempt to replace object in pool using dangling handle! Handle is {:?}, but pool record has {} generation What it means
Pool's replace-by-handle checks that the handle's generation matches the record's generation before overwriting. A generation mismatch means the handle is dangling — the object at that index was freed and the slot possibly reused — so replacing would silently write into someone else's object. The pool panics instead of corrupting data.
Solutions
- Validate the handle with pool.is_valid_handle(handle) (or try_contains) before replacing.
- Re-fetch a fresh handle after any free/spawn sequence touching the same pool.
- Subscribe to destruction events and clear stored handles when their target is freed.
Example fix
// before
pool.replace(stale_handle, new_obj); // panics on generation mismatch
// after
if pool.is_valid_handle(stale_handle) {
pool.replace(stale_handle, new_obj);
} else {
let h = pool.spawn(new_obj);
} Defensive patterns
Strategy: validation
Validate before calling
// validate before replace
if !pool.is_valid_handle(handle) {
// handle is dangling: re-fetch or respawn
} Type guard
fn is_live<T>(pool: &Pool<T>, h: Handle<T>) -> bool {
pool.try_borrow(h).is_some()
} Prevention
- Check is_valid_handle/try_borrow before every replace on long-lived handles.
- Clear stored handles on destruction events from the pool/graph.
- Treat handles as generation-checked capabilities; never cache raw indices.
When it happens
Trigger: Calling Pool::replace (or the method at fyrox-core/src/pool/mod.rs:1203) with a Handle obtained before the target was freed and its slot recycled; also after wrapping/free of the original object.
Common situations: Storing handles long-term (in components, UI, scripts) while the referenced pool object is freed and the index reused; event callbacks firing after the target was destroyed.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- The handle must be valid!
- Animation pool must be empty on load!
- An object at index must be returned to a pool it was taken…
- Attempt to spawn an object at pool record with payload!…
- Ticket index was invalid
AI-assisted analysis of FyroxEngine/Fyrox@76c91aad8e (2026-09-10).
Data as JSON: /api/errors/738eb80e8d82a728.
Report an issue: GitHub.
Appendix: source
Thrown at fyrox-core/src/pool/mod.rs:1203
///
/// [`take_reserve`]: Pool::take_reserve
/// [`alive_count`]: Pool::alive_count
#[inline]
pub fn total_count(&self) -> u32 {
let free = u32::try_from(self.free_stack.len()).expect("free stack length overflowed u32");
self.records_len() - free
}
#[inline]
pub fn replace(&mut self, handle: Handle<T>, payload: T) -> Option<T> {
let index_usize = usize::try_from(handle.index).expect("index overflowed usize");
if let Some(record) = self.records.get_mut(index_usize) {
if record.generation == handle.generation {
self.free_stack.retain(|i| *i != handle.index);
record.payload.replace(payload)
} else {
panic!("Attempt to replace object in pool using dangling handle! Handle is {:?}, but pool record has {} generation", handle, record.generation);
}
} else {
None
}
}
/// Returns a reference to the first element in the pool (if any).
pub fn first_ref(&self) -> Option<&T> {
self.iter().next()
}
/// Returns a reference to the first element in the pool (if any).
pub fn first_mut(&mut self) -> Option<&mut T> {
self.iter_mut().next()
}
/// Checks if given handle "points" to some object.
///View on GitHub (pinned to 76c91aad8e)