{"record":{"id":"bac10696e665fd28","repo":"wasmerio/wasmer","slug":"internal-error-tried-to-import-tls-symbol-from-mo","errorCode":null,"errorMessage":"Internal error: Tried to import TLS symbol from module {} that has no TLS base","messagePattern":"Internal error: Tried to import TLS symbol from module (.+?) that has no TLS base","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"lib/wasix/src/state/linker/instance_group.rs","lineNumber":345,"sourceCode":"                .unwrap_or_else(|e| {\n                    panic!(\n                        \"Internal error: failed to resolve exported function {}: {e:?}\",\n                        pending.name\n                    )\n                });\n\n            self.place_in_function_table_at(store, func.clone(), pending.function_table_index)\n                .map_err(LinkError::TableAllocationError)?;\n\n            trace!(?pending, \"Placed pending function in table\");\n        }\n\n        for tls in &pending_resolutions.tls {\n            let Some(tls_base) = self.tls_base(tls.resolved_from) else {\n                // This is a panic since this error should have been caught when the symbol\n                // was originally resolved by the instigating instance group. We're just replaying\n                // the changes.\n                panic!(\n                    \"Internal error: Tried to import TLS symbol from module {} that \\\n                    has no TLS base\",\n                    tls.resolved_from.0\n                );\n            };\n\n            let final_addr = tls_base + tls.offset;\n            set_integer_global(store, \"<pending TLS global>\", &tls.global, final_addr)?;\n            trace!(?tls, tls_base, final_addr, \"Setting pending TLS global\");\n        }\n\n        Ok(())\n    }\n\n    pub(super) fn apply_requested_symbols_from_linker(\n        &self,\n        store: &mut impl AsStoreMut,\n        linker_state: &LinkerState,","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/wasmerio/wasmer/blob/8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5/lib/wasix/src/state/linker/instance_group.rs#L327-L363","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure the exporting module exports the TLS base symbol (e.g. __tls_base) before linking side modules that import TLS symbols.","Validate TLS imports at original resolution time so failures are reported as LinkError, not replayed as panics.","Confirm the pending resolution's resolved_from handle points at the module that actually provides the TLS base.","Upgrade wasmtime if this occurs on standard shared-memory + TLS module flows; report the reproducer upstream."],"exampleFix":"// before\nlet Some(tls_base) = self.tls_base(tls.resolved_from) else { panic!(...) };\n// after (validated earlier, surface as LinkError)\nlet Some(tls_base) = self.tls_base(tls.resolved_from) else {\n    return Err(LinkError::MissingTlsBaseExport(tls.name.clone(), tls.resolved_from));\n};","handlingStrategy":"validation","validationCode":"// Before linking TLS-importing side modules, check the exporting module provides a TLS base\nif side_module.imports().any(|i| is_tls_import(&i)) {\n    assert!(main_module.exports().any(|e| e.name() == \"__tls_base\"),\n        \"main module must export a TLS base for TLS side modules\");\n}","typeGuard":null,"tryCatchPattern":"// Resolve TLS imports explicitly first so failures are LinkErrors, not replays:\nmatch group.resolve_tls_symbol(&tls) {\n    Ok(base) => base,\n    Err(LinkError::MissingTlsBaseExport(name, h)) => {\n        eprintln!(\"module {h:?} has no TLS base for {name}\");\n        return Err(LinkError::MissingTlsBaseExport(name, h));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Ensure all exporting modules used for TLS symbols export a TLS base symbol.","Validate TLS imports at original resolution time, before replay/finalization.","Avoid mixing modules built with and without thread-local storage support.","Do not reorder instance groups between resolution and apply."],"tags":["panic","tls","dynamic-linking","wasix","internal-error"],"backgroundTag":"missing-tls-base","analyzedSha":"8c4b9ee9d33fb2068863fbb3d328683e7e6ff7f5","analyzedAt":"2026-09-01T23:06:31.009Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}