Troubleshooting Common Problems
This page contains several common problems that may be encountered while using
the @wasmer/sdk
package.
SharedArrayBuffer
and Cross-Origin Isolation
If you encounter the following warning in your console while using the
@wasmer/sdk
package, it indicates an issue with Cross-Origin Isolation:
WARN wasmer_js::tasks::scheduler: An error occurred while handling a message
error=Failed to execute 'postMessage' on 'Worker': SharedArrayBuffer transfer
requires self.crossOriginIsolated.
Root Cause
This issue arises due to security measures implemented in browsers following the
Spectre and Meltdown vulnerabilities. These measures restrict the sharing of
SharedArrayBuffer
objects with Web Workers unless the execution context is
deemed secure.
The @wasmer/sdk
uses a threadpool built on Web Workers and requires sharing
the same SharedArrayBuffer
across multiple workers to enable WASIX threads to
access the same address space. This requirement is crucial even for running
single-threaded WASIX programs because the SDK internals rely on
SharedArrayBuffer
for communication with Web Workers.
Solution
To resolve this issue, your application must run in a secure context with Cross-Origin Isolation. This is achieved by setting specific HTTP response headers:
- Set 'Cross-Origin-Opener-Policy' to 'same-origin': This header ensures that your document does not share a browsing context group with cross-origin documents.
- Set 'Cross-Origin-Embedder-Policy' to 'require-corp': This header
requires all resources to be loaded with explicit CORS policies, enabling a
secure environment for
SharedArrayBuffer
.
After setting these headers, reload your application and verify if the issue is resolved.
If you are deploying your app to a server that doesn't allow setting the COOP
and COEP headers directly (e.g. GitHub Pages) you may be able to use
coi-serviceworker
as a workaround. Check out Patching COOP/COEP headers for
GitHub Pages Deployment for more.
Instantiation Failed Due to Memory Import Mismatch
If you encounter an error message similar to the one below when running
WebAssembly modules with the @wasmer/sdk
, it indicates an issue with memory
imports:
ERROR wasmer_wasix::state::env: Instantiation failed
pid=1
error=RuntimeError: js: WebAssembly.Instance():
Import #54 module="env" function="memory":
memory import has 1 page which is smaller than the declared initial of 179
This error typically appears when attempting to run a package like ffmpeg
in
the following manner:
import { init, Wasmer } from "@wasmer/sdk";
await init();
const pkg = await Wasmer.fromRegistry("wasmer/ffmpeg@1.0.5");
const ffmpeg = pkg.commands["ffmpeg"].binary();
const module = await WebAssembly.compile(ffmpeg);
let instance = await run(module, {
program: "ffmpeg",
args: ["-i", "input.mp4", "-vf", `"transpose=2"`, "output.mp4"],
});
let result = await instance.wait();
Root Cause
The issue stems from how memory is allocated for the WebAssembly module. When
using WebAssembly.compile()
and run()
directly, the browser doesn't provide
detailed information about the memory requirements of the module, such as the
number of pages needed.
Wasmer has no way of knowing how much memory the WebAssembly module is importing
(the env.memory
import), so it guesses (opens in a new tab) a minimum of 1 page and
hopes for the best.
Most of the time this works out fine, but some WebAssembly modules (like
ffmpeg
) are statically defined to require significantly more (in this case, at
least 179 pages).
Solution
To work around this, Wasmer has a wasm-types-polyfill
feature (opens in a new tab), which parses the original *.wasm
file to
determine the correct amount of memory required. To utilize this workaround, you
should pass the original *.wasm
file's contents as a Uint8Array
to the
run()
method.
import { init, Wasmer } from "@wasmer/sdk";
await init();
const pkg = await Wasmer.fromRegistry("wasmer/ffmpeg@1.0.5");
const ffmpeg = pkg.commands["ffmpeg"].binary();
let instance = await run(ffmpeg, {
program: "ffmpeg",
args: ["-i", "input.mp4", "-vf", `"transpose=2"`, "output.mp4"],
});
let result = await instance.wait();
A more straightforward and effective approach is to use the package's
entrypoint.run()
method, which automatically handles memory allocation
correctly.
Here is the recommended way to run a package's command:
const pkg = await Wasmer.fromRegistry("ffmpeg/ffmpeg");
const instance = await pkg.entrypoint.run({ args: [...] });
const output = await instance.wait();
Using this approach ensures that the necessary memory is allocated for the WebAssembly module, thereby avoiding the instantiation error.
Undefined __wbindgen_*
Reference
When working with the @wasmer/sdk
package, you might encounter the following
JavaScript error:
Uncaught TypeError: Cannot read properties of undefined (reading '__wbindgen_add_to_stack_pointer')
This error typically occurs when trying to access @wasmer/sdk
functionalities
without properly initializing the SDK.
Root Cause
The root cause of this issue is the premature usage of the @wasmer/sdk
package's functions or methods before calling the init()
function.
The init()
function is crucial as it loads the WebAssembly code that
constitutes the core logic of the @wasmer/sdk
and initializes internal states
necessary for the package, including those set up by
wasm-bindgen
(opens in a new tab).
Solution
To resolve this issue, ensure that init()
is called and awaited at the
beginning of your script or module where you intend to use the @wasmer/sdk
.
This setup should be done before any other interaction with the SDK's functionalities.
import { init, Wasmer } from "@wasmer/sdk";
async function main() {
// Ensure to await the initialization before using any SDK functionality
await init();
// After initialization, you can safely use the SDK's features
const pkg = await Wasmer.fromRegistry("some-package");
// ... rest of your code
}
main();
Unable to Determine Hardware Concurrency
When attempting to use the @wasmer/sdk
package, you might encounter the
following error:
Error: Unable to determine the hardware concurrency
at A.wbg.__wbg_new_ab87fd305ed9004b (/path/to/node_modules/@wasmer/sdk/dist/Library.cjs:11:44038)
... [additional stack trace] ...
detailedMessage: 'Unable to determine the hardware concurrency',
causes: []
This error typically arises when executing @wasmer/sdk
code within a Node.js
environment.
Root Cause
The @wasmer/sdk
package is currently designed for browser environments and
relies on specific browser features.
The error is caused by the absence of the navigator
object in Node.js, which
@wasmer/sdk
attempts to access to determine the hardware concurrency level
(specifically, navigator.hardwareConcurrency
). Since Node.js doesn't provide
this browser-specific object, the SDK cannot function as expected.
This is the first of several places where @wasmer/sdk
is not yet compatible
with Node.js.
A future release of Node.js with
nodejs/node#47769 (opens in a new tab) may resolve the
navigator.hardwareConcurrency
issue only for @wasmer/sdk
to fail at
another place where Node.js diverges from web APIs.
Solution
As of now, @wasmer/sdk
does not support Node.js.
To resolve this issue, you need to run your code in a supported browser environment rather than in Node.js.
ReferenceError: document is not defined
When making a production build of your app, you might encounter an issue where
worker threads fail to start up due to a ReferenceError
exception.
Uncaught (in promise) ReferenceError: document is not defined
at index-bnmRepzq.js:1:21
at index-bnmRepzq.js:1:708
Root Cause
This issue arises because Web Workers do not have access to the DOM, and any attempt by the worker threads to access DOM elements will result in a crash.
Something to be aware of is that each Web Worker in the threadpool will import
@wasmer/sdk
on startup to bring in the rest of its business logic.
In production mode, a bundle often includes all dependencies, causing the
@wasmer/sdk
import to also execute top-level code from your dependencies. Some
dependencies, like xterm.js
, will interact with the DOM (opens in a new tab) and
inadvertently crash the worker.
Solution
To resolve this issue, @wasmer/sdk
should be dynamically imported, ensuring
that the bundler puts it in a separate chunk from xterm.js
. This approach
prevents the worker from executing the DOM-related code in xterm.js
and allows
it to start up successfully.
Here's how you can implement the fix:
Modify your import statement
Change the static import of @wasmer/sdk
to a dynamic one. This tells the
bundler to separate it into its own chunk, avoiding the unintentional execution
of DOM-related code.
// Before
import { Wasmer, init, initializeLogger, Instance } from "@wasmer/sdk/dist/WasmerSDKBundled.js";
// After
const { Wasmer, init, initializeLogger } = await import("@wasmer/sdk/dist/WasmerSDKBundled");
Update your vite.config.js
Ensure your Vite configuration doesn't add its own code top-level which might trigger a similar error.
build: {
modulePreload: {
polyfill: false,
},
},
Rebuild and redeploy
After making these changes, rebuild your app in production mode and redeploy it.
For a real-world example of this, check out the diff for
wasmerio/wasmer-js#374
(opens in a new tab) on GitHub.