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

  1. Return the object to the pool by calling the pool's consuming free method with the ticket before it drops.
  2. If you intentionally never return it, call Pool::forget_ticket to detach the ticket safely.
  3. 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

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


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)