wasmerio/wasmer · critical

Internal error: Tried to import TLS symbol from module {} th

Error message

Internal error: Tried to import TLS symbol from module {} that has no TLS base

What it means

While replaying pending TLS symbol resolutions, this looks up the exporting module's TLS base pointer. The comment states the error should have been caught when the symbol was first resolved, so a missing TLS base at replay time means inconsistent link state — an internal invariant violation, hence a panic.

Source

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

                .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 \
                    has no TLS base",
                    tls.resolved_from.0
                );
            };

            let final_addr = tls_base + tls.offset;
            set_integer_global(store, "<pending TLS global>", &tls.global, final_addr)?;
            trace!(?tls, tls_base, final_addr, "Setting pending TLS global");
        }

        Ok(())
    }

    pub(super) fn apply_requested_symbols_from_linker(
        &self,
        store: &mut impl AsStoreMut,
        linker_state: &LinkerState,

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Ensure the exporting module exports the TLS base symbol (e.g. __tls_base) before linking side modules that import TLS symbols.
  2. Validate TLS imports at original resolution time so failures are reported as LinkError, not replayed as panics.
  3. Confirm the pending resolution's resolved_from handle points at the module that actually provides the TLS base.
  4. Upgrade wasmtime if this occurs on standard shared-memory + TLS module flows; report the reproducer upstream.

Example fix

// before
let Some(tls_base) = self.tls_base(tls.resolved_from) else { panic!(...) };
// after (validated earlier, surface as LinkError)
let Some(tls_base) = self.tls_base(tls.resolved_from) else {
    return Err(LinkError::MissingTlsBaseExport(tls.name.clone(), tls.resolved_from));
};
Defensive patterns

Strategy: validation

Validate before calling

// Before linking TLS-importing side modules, check the exporting module provides a TLS base
if side_module.imports().any(|i| is_tls_import(&i)) {
    assert!(main_module.exports().any(|e| e.name() == "__tls_base"),
        "main module must export a TLS base for TLS side modules");
}

Try / catch

// Resolve TLS imports explicitly first so failures are LinkErrors, not replays:
match group.resolve_tls_symbol(&tls) {
    Ok(base) => base,
    Err(LinkError::MissingTlsBaseExport(name, h)) => {
        eprintln!("module {h:?} has no TLS base for {name}");
        return Err(LinkError::MissingTlsBaseExport(name, h));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: apply_dl_operation/instantiate flow where a pending TLS import resolves from a module that never exported a TLS base (e.g. module linked without __tls_base, or tls_base lookup keyed on the wrong ModuleHandle in tls.resolved_from).

Common situations: Linking side modules that use thread-local storage against a main module lacking a TLS base export; custom dlopen implementations skipping the TLS-base validation pass; forks modifying instance_group resolution order.

Understand the failure class

Related errors


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