FyroxEngine/Fyrox · critical
An object at index must be returned to a pool it was taken…
Error message
An object at index {} must be returned to a pool it was taken from! Call Pool::forget_ticket if you don't need the object anymore. What it means
Pool's Ticket<T> represents a checked-out object; its Drop impl panics unconditionally because the only correct way to release the ticket is to hand the object back via Pool (free/return), which consumes the ticket without running Drop. Dropping a ticket means an object was never returned to the pool, breaking the pool's free-list invariant.
Solutions
- Return the object to the pool by calling the pool's consuming free method with the ticket before it drops.
- If you intentionally never return it, call Pool::forget_ticket to detach the ticket safely.
- Restructure error paths (early returns, ?) so the ticket is returned on all branches.
Example fix
// before
let (ticket, obj) = pool.take_and_get(handle);
if obj.is_bad() { return; } // ticket dropped -> panic
// after
let (ticket, obj) = pool.take_and_get(handle);
if obj.is_bad() {
pool.free(ticket);
return;
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure every take has a matching free on all paths
let (ticket, obj) = pool.take_and_get(h);
let result = (|| { /* work with obj */ })();
pool.free(ticket); // always runs before any return Try / catch
// Rust cannot catch panics safely; instead wrap the work so the ticket is consumed:
fn with_pooled<T, R>(pool: &mut Pool<T>, h: Handle<T>, f: impl FnOnce(&mut T) -> R) -> R {
let (ticket, mut obj) = pool.take_and_get(h);
let r = f(&mut obj);
pool.free(ticket);
r
} Prevention
- Use a closure/RAII wrapper that frees the ticket before any early return.
- Call Pool::forget_ticket when intentionally abandoning the object.
- Never store Ticket values in long-lived structs without a free path.
When it happens
Trigger: Letting a Ticket value go out of scope (or explicitly drop(ticket)) instead of returning the borrowed object to the pool with the consuming free/return method; early-returns or ? inside a scope holding a ticket.
Common situations: Error paths in pooled-resource code (connections, sound sources, temporary buffers) that forget to return the object; misuse after Pool::try_take/ take where the ticket is stored and later discarded.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Animation pool must be empty on load!
- Attempt to spawn an object at pool record with payload!…
- Attempt to replace object in pool using dangling handle!…
- Ticket index was invalid
- The handle must be valid!
AI-assisted analysis of FyroxEngine/Fyrox@76c91aad8e (2026-09-10).
Data as JSON: /api/errors/9592da3fb3770e87.
Report an issue: GitHub.
Appendix: source
Thrown at fyrox-core/src/pool/mod.rs:367
where
T: 'static,
P: PayloadContainer<Element = T> + 'static,
{
#[inline]
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Ticket<T> {
index: u32,
marker: PhantomData<T>,
}
impl<T> Drop for Ticket<T> {
fn drop(&mut self) {
panic!(
"An object at index {} must be returned to a pool it was taken from! \
Call Pool::forget_ticket if you don't need the object anymore.",
self.index
)
}
}
impl<T, P> Clone for PoolRecord<T, P>
where
T: Clone,
P: PayloadContainer<Element = T> + Clone + 'static,
{
#[inline]
fn clone(&self) -> Self {
Self {
ref_counter: Default::default(),
generation: self.generation,
payload: self.payload.clone(),View on GitHub (pinned to 76c91aad8e)