windmill-labs/windmill · error

esbuild-wasm@${version} tarball did not contain lib/main.js

Error message

esbuild-wasm@${version} tarball did not contain lib/main.js

What it means

Thrown by ensureWasmPackage after extracting the esbuild-wasm tarball: the extracted contents did not include lib/main.js, so the extracted package cannot be loaded as the esbuild JS host. The loader deletes the temp dir and refuses to cache the incomplete package. It guards against pointing WINDMILL_ESBUILD_WASM_URL at something that is not a valid esbuild-wasm npm-style tarball.

Source

Thrown at cli/src/utils/esbuild_loader.ts:143

  log.info(`Downloading esbuild-wasm@${version} from ${url} ...`);
  const res = await fetch(url);
  if (!res.ok || !res.body) {
    throw new Error(
      `Failed to download esbuild-wasm@${version} (${res.status} ${res.statusText}). ` +
        `Set WINDMILL_ESBUILD_WASM_PATH to an extracted esbuild-wasm package dir, ` +
        `point WINDMILL_ESBUILD_WASM_URL at a reachable tarball, or repair the native esbuild install.`
    );
  }

  // Extract to a unique temp dir and rename into place so a crash or a
  // concurrent writer can't leave a half-extracted package behind, and so two
  // extractions never share an in-progress directory.
  const tmpDir = `${destDir}.${process.pid}.${extractCounter++}.tmp`;
  fs.rmSync(tmpDir, { recursive: true, force: true });
  await extractTarball(res.body, tmpDir);
  if (!fs.existsSync(path.join(tmpDir, "lib", "main.js"))) {
    fs.rmSync(tmpDir, { recursive: true, force: true });
    throw new Error(`esbuild-wasm@${version} tarball did not contain lib/main.js`);
  }
  try {
    fs.renameSync(tmpDir, destDir);
  } catch {
    // Another process won the race, or rename across devices failed; clean up
    // and let the existsSync check below decide whether the cache is usable.
    fs.rmSync(tmpDir, { recursive: true, force: true });
  }
  if (!fs.existsSync(path.join(destDir, "lib", "main.js"))) {
    throw new Error(`Failed to cache esbuild-wasm@${version} at ${destDir}`);
  }
  return destDir;
}

/**
 * Resolves a tar entry to an absolute path inside destDir, stripping the leading
 * "package/" component that npm tarballs use. Returns null if the entry would
 * escape destDir (tar-slip), since WINDMILL_ESBUILD_WASM_URL allows untrusted

View on GitHub (pinned to e474e8803c)

Solutions

  1. Point WINDMILL_ESBUILD_WASM_URL at the official npm tarball layout, e.g. https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-<version>.tgz.
  2. Verify the artifact: `tar tzf file.tgz | grep lib/main.js` must list package/lib/main.js.
  3. Set WINDMILL_ESBUILD_WASM_PATH to a correctly extracted esbuild-wasm package directory instead of relying on a URL.
  4. Re-upload/fix the artifact in your internal mirror if it is truncated or the wrong package.

Example fix

// before
WINDMILL_ESBUILD_WASM_URL=https://github.com/evanw/esbuild/archive/refs/tags/v0.28.0.tar.gz
// after: use the npm-style package tarball
WINDMILL_ESBUILD_WASM_URL=https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.28.0.tgz
Defensive patterns

Strategy: validation

Validate before calling

import { execSync } from "node:child_process";
const url = process.env.WINDMILL_ESBUILD_WASM_URL;
if (url) {
  const out = execSync(`tar tzf <(curl -fsSL ${url}) 2>/dev/null || curl -fsSL ${url} | tar tz`, { shell: "/bin/bash" }).toString();
  if (!out.split("\n").some((n) => /(^|\/)lib\/main\.js$/.test(n.replace(/^package\//, "")))) {
    throw new Error(`Tarball at ${url} has no lib/main.js — use an esbuild-wasm npm package tarball`);
  }
}

Try / catch

try {
  await getEsbuild();
} catch (e) {
  if (String(e).includes("did not contain lib/main.js")) {
    console.error("WINDMILL_ESBUILD_WASM_URL must point at an esbuild-wasm npm tarball (package/lib/main.js inside)");
  }
  throw e;
}

Prevention

When it happens

Trigger: The tarball downloaded from WINDMILL_ESBUILD_WASM_URL (or a proxy-intercepted response) extracted successfully but has no lib/main.js at its root after stripping the leading package/ component — i.e. it is not an esbuild-wasm package tarball, is a wrong/truncated artifact, or has an unexpected inner layout.

Common situations: WINDMILL_ESBUILD_WASM_URL pointing at a GitHub archive, a wrong package's tarball, or an HTML error page saved as .tgz; an internal mirror serving an older tarball layout; a corrupted upload in a self-hosted mirror; a manually crafted tarball missing the package/ prefix the extractor strips.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/c6fa05c49cb57972. Report an issue: GitHub.