{"record":{"id":"aa82d04c4d3e67e0","repo":"denoland/deno","slug":"err-wasi-already-started","errorCode":"ERR_WASI_ALREADY_STARTED","errorMessage":"WASI instance has already started","messagePattern":"WASI instance has already started","errorType":"exception","errorClass":"ERR_WASI_ALREADY_STARTED","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/wasi.ts","lineNumber":688,"sourceCode":"    }\n    // deno-lint-ignore deno-internal/prefer-primordials -- WebAssembly.Memory.prototype.buffer getter; no primordial equivalent\n    return new Uint8Array(this.#memory.buffer);\n  }\n\n  get wasiImport() {\n    return this.#wasiImport;\n  }\n\n  getImportObject() {\n    if (this.#version === \"unstable\") {\n      return { wasi_unstable: this.#wasiImport };\n    }\n    return { wasi_snapshot_preview1: this.#wasiImport };\n  }\n\n  start(instance?: WebAssembly.Instance): number {\n    if (this.#started) {\n      throw new ERR_WASI_ALREADY_STARTED();\n    }\n\n    if (instance === undefined || instance === null) {\n      throw new ERR_INVALID_ARG_TYPE(\"instance\", \"object\", instance);\n    }\n    if (typeof instance !== \"object\") {\n      throw new ERR_INVALID_ARG_TYPE(\"instance\", \"object\", instance);\n    }\n\n    const exports = instance.exports;\n    if (exports === null || typeof exports !== \"object\") {\n      throw new ERR_INVALID_ARG_TYPE(\"instance.exports\", \"object\", exports);\n    }\n\n    if (typeof exports._start !== \"function\") {\n      throw new ERR_INVALID_ARG_TYPE(\n        \"instance.exports._start\",\n        \"function\",","sourceCodeStart":670,"sourceCodeEnd":706,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/node/polyfills/wasi.ts#L670-L706","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst wasi = new WASI({ version: 'preview1' });\nfor (const mod of modules) {\n  const inst = new WebAssembly.Instance(mod, wasi.getImportObject());\n  wasi.start(inst); // throws on second iteration\n}\n\n// after\nfor (const mod of modules) {\n  const wasi = new WASI({ version: 'preview1' });\n  const inst = new WebAssembly.Instance(mod, wasi.getImportObject());\n  wasi.start(inst);\n}","handlingStrategy":"validation","validationCode":"// One WASI per run: construct inside the runner, never outside.\nfunction runWasi(bytes: Buffer, preopens: Record<string, string>): number {\n  const wasi = new WASI({ version: 'preview1', preopens });\n  const module = new WebAssembly.Module(bytes);\n  const instance = new WebAssembly.Instance(module, wasi.getImportObject());\n  return wasi.start(instance);\n}","typeGuard":null,"tryCatchPattern":"try {\n  wasi.start(instance);\n} catch (err) {\n  if ((err as { code?: string }).code === 'ERR_WASI_ALREADY_STARTED') {\n    // reuse detected: build a fresh WASI and retry once with a new instance\n    const fresh = new WASI({ version: 'preview1', preopens });\n    const inst = new WebAssembly.Instance(module, fresh.getImportObject());\n    return fresh.start(inst);\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["wasi","webassembly","lifecycle","double-start","node-compat"],"backgroundTag":"double-initialization","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}