evanw/esbuild · error · Error

Failed to install package

Error message

Failed to install package "${pkg}"

What it means

In checkAndPreparePackage (lib/npm/node-install.ts:285), when require.resolve(pkg) fails to find the platform package AND the platform is a WebAssembly-fallback platform (isWASM true), esbuild throws immediately. The reason is that the generic download fallback only works for self-contained native binaries; a WASM 'binary' is actually a JS file referencing a separate esbuild.wasm, so the fallback cannot reconstitute it. This is hit on WASM-fallback platforms like android-arm when the optional dependency was skipped.

Solutions

  1. Remove the --no-optional / --omit=optional flag so the WASM-fallback platform package installs normally.
  2. Explicitly install the required @esbuild/* platform package for your environment.
  3. Switch to the esbuild-wasm package which works without a platform-specific optional dependency.

Example fix

# before
npm install esbuild --no-optional   # on a wasm-fallback platform

# after
npm install esbuild                 # let optionalDependencies resolve
Defensive patterns

Strategy: fallback

Validate before calling

// Detect a wasm-fallback platform and avoid --no-optional installs there.
const os = require('os')
const wasmFallback = new Set([
  'android arm LE', 'android x64 LE', 'openharmony arm64 LE',
])
const key = `${process.platform} ${os.arch()} ${os.endianness()}`
if (wasmFallback.has(key)) {
  console.warn('Use esbuild-wasm or install without --no-optional on this platform')
}

Prevention

When it happens

Trigger: Installing esbuild on a WASM-fallback platform (e.g. @esbuild/android-arm, android-x64, openharmony-arm64) while the optional @esbuild/* package is absent (installed with --no-optional or --omit=optional).

Common situations: Termux/Android development with --no-optional; minimal Docker images that strip optional deps; package managers configured to skip optionals.

Related errors


AI-assisted analysis of evanw/esbuild@f6058f8364 (2026-08-09). Data as JSON: /api/errors/41e9bd837853c50e. Report an issue: GitHub.

Appendix: source

Thrown at lib/npm/node-install.ts:285

  let binPath: string
  try {
    // First check for the binary package from our "optionalDependencies". This
    // package should have been installed alongside this package at install time.
    binPath = require.resolve(`${pkg}/${subpath}`)
  } catch (e) {
    console.error(`[esbuild] Failed to find package "${pkg}" on the file system

This can happen if you use the "--no-optional" flag. The "optionalDependencies"
package.json feature is used by esbuild to install the correct binary executable
for your current platform. This install script will now attempt to work around
this. If that fails, you need to remove the "--no-optional" flag to use esbuild.
`)

    // The "binary" in the WebAssembly package is not actually a binary, and is
    // not self-contained. It's a JavaScript file that references another
    // binary "esbuild.wasm" file. The fallback code below assumes that the
    // binary is self-contained, so fail now if this is a WebAssembly fallback.
    if (isWASM) throw new Error(`Failed to install package "${pkg}"`)

    // If that didn't work, then someone probably installed esbuild with the
    // "--no-optional" flag. Attempt to compensate for this by downloading the
    // package using a nested call to "npm" instead.
    //
    // THIS MAY NOT WORK. Package installation uses "optionalDependencies" for
    // a reason: manually downloading the package has a lot of obscure edge
    // cases that fail because people have customized their environment in
    // some strange way that breaks downloading. This code path is just here
    // to be helpful but it's not the supported way of installing esbuild.
    binPath = downloadedBinPath(pkg, subpath)
    try {
      console.error(`[esbuild] Trying to install package "${pkg}" using npm`)
      installUsingNPM(pkg, subpath, binPath)
    } catch (e2: any) {
      console.error(`[esbuild] Failed to install package "${pkg}" using npm: ${e2 && e2.message || e2}`)

      // If that didn't also work, then something is likely wrong with the "npm"

View on GitHub (pinned to f6058f8364)