wasmerio/wasmer · error

Failed to free closure index

Error message

Failed to free closure index: {e}

What it means

The `closure_free` syscall asks the linker to release a previously allocated closure slot in the `__indirect_function_table`. The library considers any failure from `free_closure_index` an impossible state ("Should never happen"), so instead of returning an errno it panics with the underlying error embedded in the message.

Solutions

  1. Fix the guest module to free each closure index exactly once and only after `closure_prepare` succeeded
  2. Verify the closure index passed to the syscall is within the allocated range before freeing
  3. Serialize closure allocation/free across threads or use the runtime's locking helpers to prevent double-free races
  4. Rebuild the guest module against the current wasix closure ABI if versions mismatch
Defensive patterns

Strategy: validation

Validate before calling

// Guest/host side: only free indices that were successfully prepared and not yet freed
if !allocated_closures.contains(&closure) {
    return; // skip free instead of calling closure_free with a bogus index
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| closure_free(ctx, closure)));
if result.is_err() { /* log double-free/invalid index; recover by rebuilding the instance */ }

Prevention

When it happens

Trigger: Calling the `closure_free` syscall with a closure index that is invalid, already freed, or out of range; freeing a closure in an environment whose linker state is inconsistent (e.g. after a failed dynamic-link operation); double-free of the same closure index from two threads.

Common situations: Guest code bugs that free a closure twice or free a never-prepared index; race conditions where two WASIX threads free closures concurrently; modules compiled against a different closure ABI than the runtime supports.

Related errors


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/f6315b798dcbc4a1. Report an issue: GitHub.

Appendix: source

Thrown at lib/wasix/src/syscalls/wasix/closure_free.rs:23

/// After calling this it is undefined behavior to call the function at the given index.
#[instrument(level = "trace", fields(%closure), ret)]
pub fn closure_free(
    mut ctx: FunctionEnvMut<'_, WasiEnv>,
    closure: u32,
) -> Result<Errno, WasiError> {
    WasiEnv::do_pending_operations(&mut ctx)?;

    let (env, mut store) = ctx.data_and_store_mut();

    let Some(linker) = env.inner().linker().cloned() else {
        error!("Closures only work for dynamic modules.");
        return Ok(Errno::Notsup);
    };

    let free_result = linker.free_closure_index(&mut ctx, closure);
    if let Err(e) = free_result {
        // Should never happen
        panic!("Failed to free closure index: {e}");
    }

    return Ok(Errno::Success);
}

View on GitHub (pinned to 8c4b9ee9d3)