wasmerio/wasmer · error · anyhow::Error

env.__indirect_function_table import for Wasm C API module i

Error message

env.__indirect_function_table import for Wasm C API module is not a table

What it means

Analogous to the memory case: if the module needs the imported table `env.__indirect_function_table` and the imports already contain an export of that name which is not an Extern::Table, register_imports bails. The C API layer stores the Table handle in its state and cannot substitute another extern type.

Source

Thrown at lib/c-api-imports/src/lib.rs:168

        register_wasm_c_api_imports(store, &func_env, io);

        if let Some(memory_type) = self.imported_memory_type {
            if let Some(existing) = io.get_export("env", "memory") {
                let Extern::Memory(memory) = existing else {
                    bail!("env.memory import for Wasm C API module is not a memory");
                };
                self.set_memory(store, &memory)?;
            } else {
                let memory = Memory::new(&mut *store, memory_type)?;
                io.define("env", "memory", memory.clone());
                self.set_memory(store, &memory)?;
            }
        }

        if let Some(table_type) = self.imported_table_type {
            if let Some(existing) = io.get_export("env", "__indirect_function_table") {
                let Extern::Table(table) = existing else {
                    bail!(
                        "env.__indirect_function_table import for Wasm C API module is not a table"
                    );
                };
                self.set_table(store, &table)?;
            } else {
                let table = Table::new(&mut *store, table_type, Value::FuncRef(None))?;
                io.define("env", "__indirect_function_table", table.clone());
                self.set_table(store, &table)?;
            }
        }

        Ok(())
    }

    fn set_memory(&self, store: &mut StoreMut<'_>, memory: &Memory) -> Result<()> {
        let Some(func_env) = self.func_env()? else {
            return Ok(());
        };

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Define "env" "__indirect_function_table" as an actual Table (initialized with Value::FuncRef(None))
  2. Remove or rename the conflicting non-table export
  3. Omit the pre-defined import so Wasmer creates the table from the module's declared table type

Example fix

// before
io.define("env", "__indirect_function_table", my_memory.clone());
// after
let table = Table::new(&mut store, table_type, Value::FuncRef(None))?;
io.define("env", "__indirect_function_table", table);
Defensive patterns

Strategy: validation

Validate before calling

if let Some(e) = io.get_export("env", "__indirect_function_table") {
    if !matches!(e, Extern::Table(_)) {
        return Err("__indirect_function_table is defined but not a Table");
    }
}

Type guard

fn is_table_export(e: Option<&Extern>) -> bool {
    matches!(e, Some(Extern::Table(_)))
}

Try / catch

match result {
    Err(e) if e.to_string().contains("is not a table") => fix_env_table_import_and_retry(),
    other => other?,
}

Prevention

When it happens

Trigger: Instantiating a Wasm C API module requiring __indirect_function_table while the Imports set defines "env" "__indirect_function_table" as a memory/function/global, via add_imports.

Common situations: Custom table assigned under the standard C API table name but with the wrong extern type; collision with an existing host export of the same name; copy-pasted import wiring between modules.

Related errors


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