wasmerio/wasmer · critical

Internal error: failed to resolve exported function {}: {e:?

Error message

Internal error: failed to resolve exported function {}: {e:?}

What it means

During dynamic linking of side modules, finalize_pending_resolutions_from_linker replays earlier linker resolutions: it fetches a function that was previously resolved from the exporting instance's exports. If get_function now fails, the replay diverged from the original resolution, indicating an internal state-consistency bug, so the code panics.

Source

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

                .map(|v| v as u64);

        self.complete_side_module_from_linker(prepared, tls_base, store)
    }

    pub(super) fn finalize_pending_resolutions_from_linker(
        &self,
        pending_resolutions: &PendingResolutionsFromLinker,
        store: &mut impl AsStoreMut,
    ) -> Result<(), LinkError> {
        trace!("Finalizing pending functions");

        for pending in &pending_resolutions.functions {
            let func = self
                .instance(pending.resolved_from)
                .exports
                .get_function(&pending.name)
                .unwrap_or_else(|e| {
                    panic!(
                        "Internal error: failed to resolve exported function {}: {e:?}",
                        pending.name
                    )
                });

            self.place_in_function_table_at(store, func.clone(), pending.function_table_index)
                .map_err(LinkError::TableAllocationError)?;

            trace!(?pending, "Placed pending function in table");
        }

        for tls in &pending_resolutions.tls {
            let Some(tls_base) = self.tls_base(tls.resolved_from) else {
                // This is a panic since this error should have been caught when the symbol
                // was originally resolved by the instigating instance group. We're just replaying
                // the changes.
                panic!(
                    "Internal error: Tried to import TLS symbol from module {} that \

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Verify the pending resolution's `resolved_from` instance index is the actual exporting instance recorded during link-state building.
  2. Ensure the link_state is not mutated or partially applied between resolution and finalize_pending_resolutions_from_linker.
  3. Update wasmtime/wasix — if this fires on normal dlopen flows it is an upstream bug; reproduce and report with the module pair involved.
  4. If you fork the linker, add logging of link_state.symbols at resolution time to find where the export disappeared.

Example fix

// before (state mutated between phases, resolution replays stale index)
let func = self.instance(pending.resolved_from).exports.get_function(&pending.name);
// after: re-resolve against the current link state instead of replaying blindly
let func = self.instance(current_resolution.resolved_from)
    .exports.get_function(&current_resolution.name)
    .expect("resolution recorded in link_state must exist at finalize time");
Defensive patterns

Strategy: validation

Validate before calling

// Before finalizing, verify every pending resolution still exists
for pending in &pending_resolutions.functions {
    let inst = group.instance(pending.resolved_from);
    assert!(inst.exports.get_function(&pending.name).is_ok(),
        "pending function {} missing from instance exports before finalize", pending.name);
}

Try / catch

// Internal panic; isolate at a thread boundary in custom dlopen flows:
let result = std::panic::catch_unwind(|| group.apply_dl_operation(&op));
match result {
    Ok(r) => r,
    Err(_) => return Err(LinkError::InternalResolutionReplayFailed),
}

Prevention

When it happens

Trigger: Calling apply_dl_operation / instantiate_side_module_from_link_state where a pending function resolution references an instance whose exports no longer contain `pending.name` — e.g. link_state mutated between symbol resolution and finalization, wrong instance index (resolved_from), or exports dropped/overridden before finalization.

Common situations: Custom dlopen-style dynamic linking flows in lib/wasix; buggy use of internal InstanceGroup APIs in forks/experiments; wasmtime version mismatch between code building the link state and the code replaying it.

Related errors


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