{"record":{"id":"c6f335e07e215ec9","repo":"FyroxEngine/Fyrox","slug":"the-handle-must-be-valid","errorCode":null,"errorMessage":"The handle must be valid!","messagePattern":"The handle must be valid!","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fyrox-core/src/pool/mod.rs","lineNumber":1441,"sourceCode":"            upper_bound.map(|b| u32::try_from(b).expect(\"upper_bound overflowed u32\"));\n        let mut pool = Self::with_capacity(upper_bound.unwrap_or(lower_bound));\n        for v in iter {\n            let _ = pool.spawn(v);\n        }\n        pool\n    }\n}\n\nimpl<T, U, Container> Index<Handle<U>> for Pool<T, Container>\nwhere\n    T: 'static,\n    U: ObjectOrVariant<T>,\n    Container: PayloadContainer<Element = T> + 'static,\n{\n    type Output = U;\n    #[inline]\n    fn index(&self, index: Handle<U>) -> &Self::Output {\n        self.try_get(index).expect(\"The handle must be valid!\")\n    }\n}\n\nimpl<T, U, Container> IndexMut<Handle<U>> for Pool<T, Container>\nwhere\n    T: 'static,\n    U: ObjectOrVariant<T>,\n    Container: PayloadContainer<Element = T> + 'static,\n{\n    #[inline]\n    fn index_mut(&mut self, index: Handle<U>) -> &mut Self::Output {\n        self.try_get_mut(index).expect(\"The handle must be valid!\")\n    }\n}\n\nimpl<'a, T, P> IntoIterator for &'a Pool<T, P>\nwhere\n    P: PayloadContainer<Element = T> + 'static,","sourceCodeStart":1423,"sourceCodeEnd":1459,"githubUrl":"https://github.com/FyroxEngine/Fyrox/blob/76c91aad8eca488ce527b1af707be8b3b24ad72d/fyrox-core/src/pool/mod.rs#L1423-L1459","documentation":"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.","triggerScenarios":"Indexing a Pool with Handle::NONE, an expired/freed handle (object removed so generation changed), or a handle from a different pool.","commonSituations":"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.","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"],"exampleFix":"// before\nlet node = graph[node_handle]; // panics if freed\n\n// after\nif let Some(node) = graph.try_get_node(node_handle) {\n    // use node\n}","handlingStrategy":"type-guard","validationCode":"if handle == Handle::NONE || pool.try_get(handle).is_none() { /* skip */ }","typeGuard":"fn is_alive<T>(pool: &Pool<T>, h: Handle<T>) -> bool { h != Handle::NONE && pool.try_get(h).is_some() }","tryCatchPattern":"// prefer try_get over Index entirely; panics here are not meant to be caught\nif let Some(v) = pool.try_get(h) { /* use v */ }","preventionTips":["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"],"tags":["panic","pool","handle","index"],"backgroundTag":"invalid-handle","analyzedSha":"76c91aad8eca488ce527b1af707be8b3b24ad72d","analyzedAt":"2026-09-10T16:04:01.633Z","contentChangedAt":"2026-09-10T16:04:01.633Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}