WebAssembly Target Updates: How Undefined Symbols Are Now Handled
A breaking change is coming to Rust's WebAssembly targets: the --allow-undefined flag passed to wasm-ld is being removed. Here's what that means, why it matters, and what you may need to update.
What is --allow-undefined?
All WebAssembly binaries in Rust are produced by linking with wasm-ld, which serves the same role as tools like ld, lld, and mold — taking separately compiled crates and object files and combining them into a final binary. Since WebAssembly targets were first introduced in Rust, the --allow-undefined flag has always been passed to wasm-ld. Its documented behavior is:
--allow-undefined Allow undefined symbols in linked binary. This options
is equivalent to --import-undefined and
--unresolved-symbols=ignore-all
"Undefined" here refers to symbol resolution within wasm-ld itself. Symbols in wasm-ld work similarly to those on native platforms — every Rust function has an associated symbol, and external symbols can be declared via extern "C" blocks, for example:
unsafe extern "C"
Here, mylibrary_init is an undefined symbol — one normally provided by an external component such as a compiled C library. With --allow-undefined in effect, rather than raising a linker error, wasm-ld silently converts that unresolved symbol into a WebAssembly import:
(module
(import "env" "mylibrary_init" (func $mylibrary_init))
;; ...
)
The unresolved symbol isn't flagged as an error — instead, it quietly becomes an import in the final WebAssembly module. The precise history of why this flag was introduced is somewhat murky, but it appears to have been a practical necessity in the very early days of wasm-ld support in Rust. That workaround has remained in place ever since.
What's wrong with --allow-undefined?
Passing --allow-undefined across all WebAssembly targets creates a meaningful behavioral divergence from every other platform Rust supports. On native targets, undefined symbols are a hard linker error by default. On WebAssembly, they've been silently swept under the rug — and that silence creates real problems.
When misconfiguration or mistakes produce broken WebAssembly modules instead of build errors, the failure surfaces far from its source. Some concrete examples:
-
If
mylibrary_initis mistyped asmylibraryinit, the final binary imports the misspelled symbol rather than calling the correct C symbol — with no error at build time. -
If
mylibrarywas accidentally omitted from the build,mylibrary_initbecomes an import instead of triggering a linker error indicating an unresolved symbol. -
External tools like
wasm-bindgenorwasm-tools component newdon't know how to handle"env"imports by default, and any errors they emit won't clearly trace back to the original undefined symbol in source code. -
Web developers may have encountered errors like
Uncaught TypeError: Failed to resolve module specifier "env". Relative references must start with either "/", "./", or "../".— a confusing message that's actually downstream of an unexpected"env"import caused by an unresolved symbol that should have been caught at link time.
Removing --allow-undefined brings WebAssembly targets in line with every other platform Rust supports: undefined symbols are an error, caught early, with a clear message pointing to the actual problem.
What is going to break, and how to fix it?
In practice, most projects should be unaffected. If a WebAssembly binary currently imports unexpected symbols, it almost certainly won't run in its target environment anyway — most runtimes reject modules with unresolved imports. For example, a wasm32-wasip1 binary that imports mylibrary_init will fail at runtime in most WASI runtimes. Removing --allow-undefined simply surfaces that failure earlier, at link time, with a more actionable error message.
That said, some projects may be intentionally relying on this behavior. For instance, your application might declare an external symbol like this:
unsafe extern "C"
// ...
And then perhaps some JS code that looks like:
;
Some codebases explicitly depend on --allow-undefined generating an import in the final WebAssembly binary — and that's a legitimate pattern. If your project relies on this behavior, the recommended fix is to add a #[link] attribute that explicitly declares the wasm_import_module name:
unsafe extern "C"
// ...
This preserves the original behavior, prevents wasm-ld from treating the symbol as undefined, and works correctly both before and after this change. As a quick workaround, you can also pass -Clink-arg=--allow-undefined at compile time to restore the previous behavior without modifying your source.
When is this change being made?
The removal of --allow-undefined on wasm targets is tracked in rust-lang/rust#149868. The patch is expected to land in nightly shortly, with a stable release shipping as part of Rust 1.96 on 2026-05-28. If you run into breakage as a result, please file an issue on rust-lang/rust.