{"record":{"id":"64e0e05a8440ddf9","repo":"FyroxEngine/Fyrox","slug":"attempt-to-spawn-an-object-at-pool-record-with-pay","errorCode":null,"errorMessage":"Attempt to spawn an object at pool record with payload! Record index is {free_index}","messagePattern":"Attempt to spawn an object at pool record with payload! Record index is (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"fyrox-core/src/pool/mod.rs","lineNumber":646,"sourceCode":"                });\n\n                Ok(Handle::new(index, generation))\n            }\n        }\n    }\n\n    #[inline]\n    #[must_use]\n    /// Construct a value with the handle it would be given.\n    /// Note: Handle is _not_ valid until function has finished executing.\n    pub fn spawn_with<F: FnOnce(Handle<T>) -> T>(&mut self, callback: F) -> Handle<T> {\n        if let Some(free_index) = self.free_stack.pop() {\n            let record = self\n                .records_get_mut(free_index)\n                .expect(\"free stack contained invalid index\");\n\n            if record.payload.is_some() {\n                panic!(\n                    \"Attempt to spawn an object at pool record with payload! Record index is {free_index}\"\n                );\n            }\n\n            let generation = record.generation + 1;\n            let handle = Handle {\n                index: free_index,\n                generation,\n                type_marker: PhantomData,\n            };\n\n            let payload = callback(handle);\n\n            record.generation = generation;\n            record.payload.replace(payload);\n            handle\n        } else {\n            // No free records, create new one","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/FyroxEngine/Fyrox/blob/76c91aad8eca488ce527b1af707be8b3b24ad72d/fyrox-core/src/pool/mod.rs#L628-L664","documentation":"Pool::spawn_with reuses a record popped from the free stack and asserts the record is truly empty (no payload). Finding a payload there means the free-list and record states are out of sync — an internal invariant violation indicating pool corruption (double-free, use-after-free of handles, or manual record tampering).","triggerScenarios":"Calling spawn (or spawn_with directly) when the free stack contains an index whose record still holds a payload — normally only reachable through pool corruption or unsafe misuse.","commonSituations":"Bugs in code that manipulates the free stack or payloads directly; a double-free that pushed an index while the object was still alive; unsafe code writing payloads without updating the free stack.","solutions":["Audit recent pool operations (free/forget_ticket/unsafe access) for double-free or out-of-order release.","Rebuild the pool's bookkeeping by recreating the pool instead of continuing with corrupted state.","Report it upstream with a minimal reproducer if it occurs without unsafe code — it indicates a fyrox-core bug."],"exampleFix":"// before\nunsafe { pool.records_get_mut(idx).payload = Some(obj); } // corrupts free list\npool.spawn(obj); // panics on reused record\n// after\nlet handle = pool.spawn(obj); // use the public API","handlingStrategy":"validation","validationCode":"// only safe API usage:\nassert!(pool.try_contains(handle) || pool.is_valid_handle(handle));\n// never manipulate records/free stack directly","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use only public spawn/free APIs; never write record.payload via unsafe.","Free each handle exactly once; track freed handles.","If corruption is suspected, recreate the pool rather than continuing."],"tags":["pool","invariant","memory","panic"],"backgroundTag":"internal-invariant-violation","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"}