wasmerio/wasmer · error

Internal error: function table index {index} already occupie

Error message

Internal error: function table index {index} already occupied

What it means

place_in_function_table_at panics when trying to write a resolved function reference into an element of the indirect function table that already holds a live FuncRef. The linker assumes each allocated table slot is empty before placement; a non-null entry means the allocation/placement bookkeeping is out of sync. It is an internal invariant check for the dynamic-linking (dlopen-style) function table.

Source

Thrown at lib/wasix/src/state/linker/instance_group/table.rs:88

            ?func,
            "Placing function in table at pre-defined index"
        );

        let table = &self.indirect_function_table;
        let size = table.size(store);

        if size <= index {
            let delta = index - size + 1;
            trace!(
                current_size = ?size,
                ?delta,
                "Growing indirect function table"
            );
            table.grow(store, delta, Value::FuncRef(None))?;
        } else {
            let existing = table.get(store, index).unwrap();
            if let Value::FuncRef(Some(_)) = existing {
                panic!("Internal error: function table index {index} already occupied");
            }
        }

        let ty = func.ty(store).to_string();
        trace!(?index, ?ty, "Placing function in table at index");
        table.set(store, index, Value::FuncRef(Some(func)))
    }

    pub(super) fn allocate_function_table_for_existing_module(
        &mut self,
        linker_state: &LinkerState,
        store: &mut impl AsStoreMut,
        module_handle: ModuleHandle,
    ) -> Result<(), LinkError> {
        if self.side_instances.contains_key(&module_handle) {
            panic!(
                "Internal error: Module with handle {module_handle:?} \
                was already instantiated in this group"

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Ensure each shared module is instantiated only once per InstanceGroup (deduplicate dlopen of the same path)
  2. Recompile the shared modules so their dylink metadata (table_size, table_base, alignment) is consistent with the current compiler version
  3. Avoid calling finalize_pending_resolutions_from_linker / populate_imports_from_linker more than once for the same group
  4. Clear or rebuild the instance group if it was reused across link runs

Example fix

// before: reusing the same instance group for a second dlopen of the same module
group.apply_dl_operation(DlOperation::Load { module: same_handle, ... });
// after: check before loading
if !group.contains_module(&same_handle) {
    group.apply_dl_operation(DlOperation::Load { module: same_handle, ... });
}
Defensive patterns

Strategy: validation

Validate before calling

fn slot_is_free(store: &impl AsStoreRef, table: &Table, index: u64) -> bool {
    !matches!(table.get(store, index), Some(Value::FuncRef(Some(_))))
}
// check before placement: if !slot_is_free(store, &table, index) { skip or reallocate }

Type guard

fn is_free_slot(v: Option<&Value>) -> bool {
    !matches!(v, Some(Value::FuncRef(Some(_))))
}

Prevention

When it happens

Trigger: apply_dl_operation / populate_imports_from_linker / finalize_pending_resolutions_from_linker placing two resolved functions at the same table index; re-running apply_resolved_function for an index that was already populated; a table_base misalignment causing index overlap.

Common situations: Loading the same shared module twice into one instance group; table_size/table_alignment metadata (dylink info) of the compiled shared module not matching what the linker assumed; repeated finalize calls on the same linker state.

Related errors


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