FyroxEngine/Fyrox · error
The handle must be valid!
Error message
The handle must be valid!
What it means
The Index trait implementation for Pool makes pool[handle] syntax return the payload directly. Because indexing must return a reference (not a Result), an invalid or dangling handle causes a panic instead of a recoverable error; the safe alternative is try_get.
Solutions
- Use pool.try_get(handle) or graph.try_get_node(handle) and handle None
- Check handle != Handle::NONE before indexing
- Re-validate stored handles each frame / after deletions instead of caching validity
Example fix
// before
let node = graph[node_handle]; // panics if freed
// after
if let Some(node) = graph.try_get_node(node_handle) {
// use node
} Defensive patterns
Strategy: type-guard
Validate before calling
if handle == Handle::NONE || pool.try_get(handle).is_none() { /* skip */ } Type guard
fn is_alive<T>(pool: &Pool<T>, h: Handle<T>) -> bool { h != Handle::NONE && pool.try_get(h).is_some() } Try / catch
// prefer try_get over Index entirely; panics here are not meant to be caught
if let Some(v) = pool.try_get(h) { /* use v */ } Prevention
- Default to try_get/try_get_mut in gameplay code
- Drop or invalidate cached handles when the object is deleted
- Never index with Handle::NONE
When it happens
Trigger: Indexing a Pool with Handle::NONE, an expired/freed handle (object removed so generation changed), or a handle from a different pool.
Common situations: Storing handles to scene nodes/widgets and using them after the object was deleted (e.g. node removed from graph but still referenced by a script or UI callback); default/zero handles used unchecked.
Related errors
- Attempt to replace object in pool using dangling handle!…
- 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/c6f335e07e215ec9.
Report an issue: GitHub.
Appendix: source
Thrown at fyrox-core/src/pool/mod.rs:1441
upper_bound.map(|b| u32::try_from(b).expect("upper_bound overflowed u32"));
let mut pool = Self::with_capacity(upper_bound.unwrap_or(lower_bound));
for v in iter {
let _ = pool.spawn(v);
}
pool
}
}
impl<T, U, Container> Index<Handle<U>> for Pool<T, Container>
where
T: 'static,
U: ObjectOrVariant<T>,
Container: PayloadContainer<Element = T> + 'static,
{
type Output = U;
#[inline]
fn index(&self, index: Handle<U>) -> &Self::Output {
self.try_get(index).expect("The handle must be valid!")
}
}
impl<T, U, Container> IndexMut<Handle<U>> for Pool<T, Container>
where
T: 'static,
U: ObjectOrVariant<T>,
Container: PayloadContainer<Element = T> + 'static,
{
#[inline]
fn index_mut(&mut self, index: Handle<U>) -> &mut Self::Output {
self.try_get_mut(index).expect("The handle must be valid!")
}
}
impl<'a, T, P> IntoIterator for &'a Pool<T, P>
where
P: PayloadContainer<Element = T> + 'static,View on GitHub (pinned to 76c91aad8e)