wasm32-wasip1-threads: wasi-sdk 34 is incompatible with Rust < 1.100 (unresolvable env.__wasi_init_tp import)
Summary
Building a napi-rs addon for wasm32-wasip1-threads with WASI_SDK_PATH pointing at wasi-sdk 34 produces a wasm module that cannot instantiate. The module carries an unresolvable import:
LinkError: WebAssembly.Instance(): Import #142 "env" "__wasi_init_tp": function import requires a callableThis is a version skew between the Rust toolchain and wasi-sdk, not a defect in napi-rs or emnapi. Nothing in @napi-rs/wasm-runtime, @emnapi/core, @emnapi/runtime, @emnapi/wasi-threads or @tybys/wasm-util can supply the symbol, because upstream deleted it rather than handing it to the embedder.
Filing it here so the constraint is written down, since the failure looks like a napi-rs loader bug.
Root cause
wasi-libc #820 — "Adjust how TLS initialization works" (7d831af67cc41327a826b446e90eff8fbe4fb601, merged 2026-07-15) removed __wasi_init_tp. TLS now initializes lazily inside pthread_self():
static inline pthread_t __pthread_self() {
pthread_t ret = (pthread_t) __get_tp();
if (ret->self != ret) // not yet initialized
__init_tp(ret);
return ret;
}__init_tp is hidden, so it is archive-internal. The symbol was dropped from expected/wasm32-wasip1-threads/defined-symbols.txt and never added to undefined-symbols.txt. There is no new embedder contract to implement.
wasi-sdk 34 (released 2026-08-25) is the first release carrying it.
$ llvm-nm wasi-sdk-33/share/wasi-sysroot/lib/wasm32-wasip1-threads/libc.a | grep __wasi_init_tp
00000001 T __wasi_init_tp # libc.a(__init_tls.c.obj) — DEFINED
$ llvm-nm wasi-sdk-34/share/wasi-sysroot/lib/wasm32-wasip1-threads/libc.a | grep __wasi_init_tp
# absent from the entire SDK treeRust still ships its own pre-34 startup objects. On stable 1.98.0:
$ llvm-nm $(rustc --print sysroot)/lib/rustlib/wasm32-wasip1-threads/lib/self-contained/crt1-reactor.o
U __wasi_init_tp # still calls it
$ llvm-nm $(rustc --print sysroot)/lib/rustlib/wasm32-wasip1-threads/lib/self-contained/libc.a
00000001 T __wasi_init_tp # Rust's bundled wasi-libc still defines itSetting WASI_SDK_PATH makes the CLI link with the wasi-sdk sysroot while cargo still contributes Rust's crt1, so the call has no definition:
Rust 1.98 crt1-reactor.o calls __wasi_init_tp
|
+--> wasm-ld --> env.__wasi_init_tp (unsatisfiable)
|
wasi-sdk 34 libc.a no longer defines itDisassembling _initialize in the built artifact confirms the stale startup path:
sdk 33 era crt1 _initialize: call env.__wasi_init_tp ; call __wasm_call_ctors
sdk 34 crt1 _initialize: call __wasm_call_ctorsAffected versions
| Component | Status |
|---|---|
Rust stable 1.98.x, beta |
affected — crt1 built with wasi-sdk 33 |
| Rust nightly >= 2026-08-27 | fixed — rust-lang/rust#161773 "Update WASI targets to wasi-sdk-34", merged 2026-08-26 |
| Rust 1.100 | first stable with the fix (src/version on main is 1.100.0) |
| wasi-sdk <= 33 | not affected |
| wasi-sdk >= 34 | affected with any pre-1.100 Rust |
wasi-sdk 34's release notes do not mention the change. It is folded into "Update to the latest wasi-libc".
Reproduction
Note the ordering: with @napi-rs/cli 3.9.0 you hit a different wasi-sdk 34 problem first (see below). Fix or bypass that one, and this is what remains.
rustup default 1.98.0
rustup target add wasm32-wasip1-threads
wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-34/wasi-sdk-34.0-x86_64-linux.tar.gz
tar -xf wasi-sdk-34.0-x86_64-linux.tar.gz
export WASI_SDK_PATH="$(pwd)/wasi-sdk-34.0-x86_64-linux"
napi build --platform --release --target wasm32-wasip1-threads # links fine
node -e "require('./<name>.wasi.cjs')" # LinkErrorConfirm the import is present:
const m = new WebAssembly.Module(require('fs').readFileSync('./<name>.wasm32-wasi.wasm'))
WebAssembly.Module.imports(m).filter((i) => i.name === '__wasi_init_tp')
// [ { module: 'env', name: '__wasi_init_tp', kind: 'function' } ]Caution when verifying a fix: NAPI_RS_FORCE_WASI=true silently falls back to the native binding when the WASI binding fails to load. Use NAPI_RS_FORCE_WASI=error, which does not fall back. I lost time to exactly this.
Workarounds
- Keep wasi-sdk <= 33 until Rust 1.100 is stable. Least surprising.
- Use Rust nightly >= 2026-08-27 with wasi-sdk 34. Matches upstream.
- Stub the import in the loader's
overwriteImports:importObject.env.__wasi_init_tp = () => {}. This happens to be safe on wasi-sdk 34 because TLS self-initializes on the firstpthread_self(), but it papers over a toolchain skew and would silently do the wrong thing on any other combination. I would not recommend shipping it. For comparison, bytecodealliance/wasm-tools#2616 hit the same shape and rebuilt the artifact rather than stubbing.
Suggestion for napi-rs
No code fix is possible here, but the failure is very hard to read. Two options worth considering:
- When
WASI_SDK_PATHreports major >= 34 and the activerustcpredates the wasi-sdk 34 update, emit a warning that names this skew. The rustc side is detectable from$(rustc --print sysroot)/lib/rustlib/wasm32-wasip1-threads/lib/self-contained/crt1-reactor.ostill referencing__wasi_init_tp. - Or document the wasi-sdk <= 33 constraint next to the
WASI_SDK_PATHdocs until 1.100 ships.
Happy to send a PR for whichever you prefer.
Related but separate
wasi-sdk 34 also broke the emnapi archive selection, through a different wasi-libc change (#846, which dropped the unused int op from the futex helpers):
wasm-ld: function signature mismatch: __wasilibc_futex_wait_atomic_wait
>>> defined as (i32, i32, i32, i64) -> i32 in emnapi/lib/wasm32-wasip1-threads/libemnapi-napi-rs-mt.a(wasi_wait.c.obj)
>>> defined as (i32, i32, i64) -> i32 in wasi-sdk-34/.../libc.a(futex.c.obj)emnapi already ships a second archive set at emnapi/lib/wasm32-wasip1-threads-wasi-sdk-34/, but setWasiEnv() in cli/src/api/build.ts derives the directory from the target triple alone and never picks it. That one is fixable in napi-rs and I have a change ready; I will open it separately so the two issues stay untangled.
Environment
@napi-rs/cli 3.9.0
emnapi 2.0.0-alpha.5
@emnapi/core 2.0.0-alpha.5
rustc 1.98.0 (88d9e12ae 2026-08-18)
wasi-sdk 34.0 (also verified against 27, 30, 32, 33)Source: napi-rs/napi-rs