denoland/deno · error · ERR_WASI_ALREADY_STARTED
ERR_WASI_ALREADY_STARTED
ERR_WASI_ALREADY_STARTED
Error message
WASI instance has already started
What it means
ERR_WASI_ALREADY_STARTED is thrown by start() when this.#started is already true. The flag is a one-way latch shared by start(), initialize(), and finalizeBindings(): once any of them has bound memory, re-entering start() on the same WASI object is invalid. Each WebAssembly module run needs a fresh WASI instance, matching Node's behavior.
Source
Thrown at ext/node/polyfills/wasi.ts:688
}
// deno-lint-ignore deno-internal/prefer-primordials -- WebAssembly.Memory.prototype.buffer getter; no primordial equivalent
return new Uint8Array(this.#memory.buffer);
}
get wasiImport() {
return this.#wasiImport;
}
getImportObject() {
if (this.#version === "unstable") {
return { wasi_unstable: this.#wasiImport };
}
return { wasi_snapshot_preview1: this.#wasiImport };
}
start(instance?: WebAssembly.Instance): number {
if (this.#started) {
throw new ERR_WASI_ALREADY_STARTED();
}
if (instance === undefined || instance === null) {
throw new ERR_INVALID_ARG_TYPE("instance", "object", instance);
}
if (typeof instance !== "object") {
throw new ERR_INVALID_ARG_TYPE("instance", "object", instance);
}
const exports = instance.exports;
if (exports === null || typeof exports !== "object") {
throw new ERR_INVALID_ARG_TYPE("instance.exports", "object", exports);
}
if (typeof exports._start !== "function") {
throw new ERR_INVALID_ARG_TYPE(
"instance.exports._start",
"function",View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Construct a new `new WASI({ version: 'preview1', ... })` for every module instantiation/run.
- If you call initialize() for reactors, do not also call start() on the same object — pick one lifecycle per instance.
- Remove any caching/singleton pattern around the WASI object.
Example fix
// before
const wasi = new WASI({ version: 'preview1' });
for (const mod of modules) {
const inst = new WebAssembly.Instance(mod, wasi.getImportObject());
wasi.start(inst); // throws on second iteration
}
// after
for (const mod of modules) {
const wasi = new WASI({ version: 'preview1' });
const inst = new WebAssembly.Instance(mod, wasi.getImportObject());
wasi.start(inst);
} Defensive patterns
Strategy: validation
Validate before calling
// One WASI per run: construct inside the runner, never outside.
function runWasi(bytes: Buffer, preopens: Record<string, string>): number {
const wasi = new WASI({ version: 'preview1', preopens });
const module = new WebAssembly.Module(bytes);
const instance = new WebAssembly.Instance(module, wasi.getImportObject());
return wasi.start(instance);
} Try / catch
try {
wasi.start(instance);
} catch (err) {
if ((err as { code?: string }).code === 'ERR_WASI_ALREADY_STARTED') {
// reuse detected: build a fresh WASI and retry once with a new instance
const fresh = new WASI({ version: 'preview1', preopens });
const inst = new WebAssembly.Instance(module, fresh.getImportObject());
return fresh.start(inst);
}
throw err;
} Prevention
- Treat WASI instances as single-use: fresh instance per module run.
- Do not cache or singleton the WASI object in task runners or request handlers.
- Pick exactly one of start/initialize/finalizeBindings per WASI object.
When it happens
Trigger: Calling `wasi.start(instanceA)` then `wasi.start(instanceB)` on the same wasi object; calling initialize() after start() on the same object; calling start() on a wasi already bound via finalizeBindings() in a thread-spawn setup.
Common situations: Looping over several wasm modules while reusing one WASI instance 'for efficiency'; a task runner that caches the WASI object across requests; mixing reactor (initialize) and command (start) paths against one instance.
Related errors
- ERR_WASI_NOT_STARTED
- ERR_INVALID_ARG_TYPE
- ERR_INVALID_ARG_VALUE
- ERR_HTTP_HEADERS_SENT
- ERR_CRYPTO_INVALID_STATE
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/aa82d04c4d3e67e0.
Report an issue: GitHub.