Rust's WebAssembly targets are about to undergo a significant change that may affect existing projects. All WebAssembly targets in Rust have historically been linked using the --allow-undefined flag passed to wasm-ld, and this flag is now being removed. This post explains what's changing, why it matters, and how to prepare.
What is --allow-undefined?
When Rust compiles WebAssembly binaries, it uses wasm-ld as the linker—similar to how ld, lld, or mold work on native platforms. The linker combines separately compiled crates and object files into a single executable binary. Since WebAssembly support was first introduced in Rust, the --allow-undefined flag has been passed to wasm-ld by default. According to its documentation:
--allow-undefined Allow undefined symbols in linked binary. This options
is equivalent to --import-undefined and
--unresolved-symbols=ignore-all
The term "undefined" refers to symbol resolution within wasm-ld. Symbols in WebAssembly work much like they do on native platforms—every Rust function has an associated symbol. You can reference external symbols in Rust using extern "C" blocks:
unsafe extern "C"
Here, mylibrary_init is an undefined symbol. Normally, this would be provided by another component—like an externally compiled C library. With --allow-undefined, however, the linker doesn't require a definition. Instead, it generates a WebAssembly module that imports the symbol:
(module
(import "env" "mylibrary_init" (func $mylibrary_init))
;; ...
)
The undefined symbol becomes an imported symbol in the final WebAssembly module. While the exact historical reasoning is unclear, --allow-undefined was apparently necessary in the early days of WebAssembly support in Rust. This workaround has persisted ever since.
What's wrong with --allow-undefined?
By passing --allow-undefined on all WebAssembly targets, rustc creates inconsistent behavior between WebAssembly and other platforms. The main problem is that build configuration errors or typos can produce broken WebAssembly modules instead of compilation errors. This delays problem discovery and makes debugging harder. Common issues include:
- Typos in symbol names: If
mylibrary_initis mistyped asmylibraryinit, the final binary imports the wrong symbol instead of calling the correctly linked C function. - Missing dependencies: If
mylibraryisn't compiled and linked,mylibrary_initbecomes an import rather than triggering a linker error. - Tooling confusion: External tools like
wasm-bindgenorwasm-tools component newdon't know how to handle"env"imports by default, producing error messages that don't clearly point back to the source of the undefined symbol. - Cryptic browser errors: Web developers may encounter errors like
Uncaught TypeError: Failed to resolve module specifier "env". Relative references must start with either "/", "./", or "../".when"env"leaks into the final module unexpectedly—the real issue is the undefined symbol, not the missing"env"provider.
On native platforms, undefined symbols are errors by default. By using --allow-undefined, rustc introduces surprising WebAssembly-specific behavior. Removing this flag aligns WebAssembly with native platform conventions and provides clearer error messages.
What is going to break, and how to fix?
In theory, this change shouldn't break much. If a WebAssembly binary imports unexpected symbols, it likely won't run in its target environment anyway, since that environment probably doesn't provide those symbols. For instance, if you compile for wasm32-wasip1 and the binary imports mylibrary_init, most runtimes will fail with an unresolved import error. In these cases, the change simply provides better diagnostics earlier in the build process.
However, some users may be intentionally relying on this behavior. For example, your application might include:
unsafe extern "C"
// ...
And corresponding JavaScript code that provides:
;
In practice, developers can intentionally depend on --allow-undefined to generate imports in the final WebAssembly binary. When this behavior is relied upon, the solution is straightforward: add a #[link] attribute that explicitly declares the wasm_import_module name.
unsafe extern "C"
// ...
This approach maintains the same runtime behavior while eliminating the undefined symbol issue for wasm-ld. The code remains compatible both before and after the change. Alternatively, developers can compile with -Clink-arg=--allow-undefined to temporarily restore the previous behavior.
When is this change being made?
The removal of --allow-undefined for wasm targets is implemented in rust-lang/rust#149868. The change will land in nightly shortly and ship with Rust 1.96 on May 28, 2026. If you encounter issues related to this change, report them on rust-lang/rust.