evanw/esbuild · error · Error

Failed to download ${JSON.stringify(wasm)}

Error message

Failed to download ${JSON.stringify(wasm)}

What it means

In the browser/wasm worker (lib/shared/worker.ts), when esbuild is given a wasm URL string instead of a precompiled WebAssembly.Module, it fetches that URL and at lib/shared/worker.ts:90 throws if the response is not ok (non-2xx). The message echoes the wasm URL via JSON.stringify. This is the browser bootstrap path: the worker downloads esbuild.wasm, and any network failure (404, CORS, offline, wrong base URL) surfaces here.

Source

Thrown at lib/shared/worker.ts:90

    instance => {
      postMessage(null)
      go.run(instance)
    },
    error => {
      postMessage(error)
    },
  )

  return go
}

async function tryToInstantiateModule(wasm: WebAssembly.Module | string, go: Go): Promise<WebAssembly.Instance> {
  if (wasm instanceof WebAssembly.Module) {
    return WebAssembly.instantiate(wasm, go.importObject)
  }

  const res = await fetch(wasm)
  if (!res.ok) throw new Error(`Failed to download ${JSON.stringify(wasm)}`)

  // Attempt to use the superior "instantiateStreaming" API first
  if ('instantiateStreaming' in WebAssembly && /^application\/wasm($|;)/i.test(res.headers.get('Content-Type') || '')) {
    const result = await WebAssembly.instantiateStreaming(res, go.importObject)
    return result.instance
  }

  // Otherwise, fall back to the inferior "instantiate" API
  const bytes = await res.arrayBuffer()
  const result = await WebAssembly.instantiate(bytes, go.importObject)
  return result.instance
}

View on GitHub (pinned to 6ff1d8b0d8)

Solutions

  1. Verify the wasm URL resolves (open it in the browser network tab) and returns 200 with Content-Type application/wasm.
  2. Host esbuild.wasm same-origin or ensure the serving origin sends Access-Control-Allow-Origin.
  3. Pass a precompiled WebAssembly.Module (compiled via WebAssembly.compile) to bypass the fetch entirely.
  4. If using a bundler, ensure the .wasm file is emitted to the output directory (copy-webpack-plugin, vite static asset handling).

Example fix

// before
import * as esbuild from 'esbuild-wasm';
await esbuild.initialize({ wasmURL: 'https://cdn.example/esbuild.wasm' }); // 404

// after — local asset + module
import wasmUrl from 'esbuild-wasm/esbuild.wasm?url';
await esbuild.initialize({ wasmURL: wasmUrl });
// or precompile:
const resp = await fetch(wasmUrl);
const mod = await WebAssembly.compile(await resp.arrayBuffer());
await esbuild.initialize({ wasmModule: mod });
Defensive patterns

Strategy: try-catch

Validate before calling

async function loadEsbuildWasm(wasmURL) {
  const resp = await fetch(wasmURL, { method: 'HEAD' });
  if (!resp.ok && resp.status !== 405) {
    throw new Error(`esbuild wasm not reachable at ${wasmURL} (status ${resp.status})`);
  }
  // ok to proceed
}

Type guard

function isWasmModule(v): v is WebAssembly.Module {
  return typeof WebAssembly !== 'undefined' && v instanceof WebAssembly.Module;
}

Try / catch

try {
  await esbuild.initialize({ wasmURL });
} catch (e) {
  if (/Failed to download/.test(e.message)) {
    // precompile and retry with a module to bypass fetch
    const r = await fetch(wasmURL);
    const mod = await WebAssembly.compile(await r.arrayBuffer());
    await esbuild.initialize({ wasmModule: mod });
  } else throw e;
}

Prevention

When it happens

Trigger: Initializing esbuild-wasm in the browser where the wasm asset path is wrong (missing CDN file, 404). CORS blocking the fetch because the server didn't send Access-Control-Allow-Origin. Offline development / network blocked. Serving wasm from a path that requires a base href the bundler didn't set.

Common situations: Vite/Webpack copies JS but not the .wasm file; the fetch 404s. CDN URL points to a tag that was unpublished. Browser blocks cross-origin wasm fetch without CORS headers. Service worker intercepts and returns a non-2xx.

Related errors


AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03). Data as JSON: /data/errors/871bad35dd778c3e.json. Report an issue: GitHub.