vitejs/vite · error · Error

Failed to decode base64-encoded data URL, Buffer and atob…

Error message

Failed to decode base64-encoded data URL, Buffer and atob are not supported

What it means

wasmHelper decodes a base64 'data:' URL by trying Node's Buffer.from first, then the global atob. If neither is available it throws, because it cannot turn the data URL into bytes for WebAssembly.instantiate. This path runs client-side when a wasm import resolves to an inline data URL.

Solutions

  1. Run the wasm import in an environment that provides atob (modern browsers) or Buffer (Node).
  2. Polyfill atob (or Buffer) in the target runtime before the wasm module loads.
  3. Avoid inlining the wasm (so the data-URL branch is not taken) — configure assets so the wasm is emitted as a file and fetched via URL instead.
  4. If using a custom JS runtime for SSR/tests, enable its fetch/Buffer shims.

Example fix

// before — runtime lacks both Buffer and atob
// after — polyfill atob before app code
import { atobPolyfill } from './atob-polyfill'
globalThis.atob = globalThis.atob || atobPolyfill
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure decode primitives exist before app boots
function assertBase64Decoders() {
  if (typeof Buffer !== 'function' && typeof atob !== 'function') {
    throw new Error('Polyfill Buffer or atob before loading wasm data URLs')
  }
}
assertBase64Decoders()

Type guard

function hasBase64Decoder(): boolean {
  return (typeof Buffer === 'function' && typeof Buffer.from === 'function') || typeof atob === 'function'
}

Prevention

When it happens

Trigger: Importing a .wasm module that gets inlined as a base64 data URL, in a runtime that has neither Buffer (Node-only) nor atob (very old or non-standard JS envs). The wasmHelper is shipped as a string and evaluated in the target runtime, so it depends on those globals being present.

Common situations: Embedding Vite-built wasm in a restricted/legacy runtime, a custom SSR environment without Node globals, or a test runner that strips globals. Browsers have atob, so this mostly bites non-browser embedded runtimes.

Understand the failure class

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/2a330175fe51f01b. Report an issue: GitHub.

Appendix: source

Thrown at packages/vite/src/node/plugins/wasm.ts:54

  ...wasmCompileOptions.builtins.map((name) => `wasm:${name}`),
  wasmCompileOptions.importedStringConstants,
])

const wasmHelper = async (opts = {}, url: string) => {
  let result
  if (url.startsWith('data:')) {
    const urlContent = url.replace(/^data:.*?base64,/, '')
    let bytes
    if (typeof Buffer === 'function' && typeof Buffer.from === 'function') {
      bytes = Buffer.from(urlContent, 'base64')
    } else if (typeof atob === 'function') {
      const binaryString = atob(urlContent)
      bytes = new Uint8Array(binaryString.length)
      for (let i = 0; i < binaryString.length; i++) {
        bytes[i] = binaryString.charCodeAt(i)
      }
    } else {
      throw new Error(
        'Failed to decode base64-encoded data URL, Buffer and atob are not supported',
      )
    }
    result = await WebAssembly.instantiate(bytes, opts, wasmCompileOptions)
  } else {
    result = await instantiateFromUrl(url, opts)
  }
  return result.instance
}

const wasmHelperCode = wasmHelper.toString()

const instantiateFromUrl = async (url: string, opts?: WebAssembly.Imports) => {
  // https://github.com/mdn/webassembly-examples/issues/5
  // WebAssembly.instantiateStreaming requires the server to provide the
  // correct MIME type for .wasm files, which unfortunately doesn't work for
  // a lot of static file servers, so we just work around it by getting the
  // raw buffer.

View on GitHub (pinned to b4d66fee14)