windmill-labs/windmill · error
Failed to download esbuild-wasm@${version} (${res.status} ${
Error message
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. What it means
This error is thrown by ensureWasmPackage in cli/src/utils/esbuild_loader.ts when the CLI's esbuild-wasm fallback cannot download the esbuild-wasm npm tarball. The fallback activates only after native esbuild fails its smoke test (e.g. host/binary version mismatch), so this error means the native esbuild is broken AND the wasm rescue download failed. The thrown message carries the HTTP status/statusText and the three configured escape hatches (WINDMILL_ESBUILD_WASM_PATH, WINDMILL_ESBUILD_WASM_URL, or repairing native esbuild).
Source
Thrown at cli/src/utils/esbuild_loader.ts:128
* and extracts the npm tarball.
*/
async function ensureWasmPackage(version: string): Promise<string> {
// Explicit local override wins (air-gapped / self-hosted workers): a path to
// an already-extracted esbuild-wasm package directory.
const override = process.env.WINDMILL_ESBUILD_WASM_PATH;
if (override) return override;
const destDir = path.join(cacheDir(), `esbuild-wasm-${version}`);
if (fs.existsSync(path.join(destDir, "lib", "main.js"))) {
return destDir;
}
const url = process.env.WINDMILL_ESBUILD_WASM_URL ??
`https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-${version}.tgz`;
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);View on GitHub (pinned to e474e8803c)
Solutions
- Fix network access to the registry (check proxy/firewall/DNS; verify with `curl -I https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.28.0.tgz`).
- Set WINDMILL_ESBUILD_WASM_PATH to an already-extracted esbuild-wasm package directory containing lib/main.js (works fully offline).
- Set WINDMILL_ESBUILD_WASM_URL to a reachable tarball (internal mirror or a manually downloaded .tgz).
- Repair the native esbuild install (`npm rebuild esbuild` or reinstall the CLI) so the wasm fallback is never needed.
Example fix
// before: broken native install + no network path // CLI falls back to wasm download and fails // after: provide an offline override export WINDMILL_ESBUILD_WASM_PATH=/opt/vendor/esbuild-wasm-0.28.0 # dir must contain lib/main.js, bin/esbuild, esbuild.wasm
Defensive patterns
Strategy: fallback
Validate before calling
// Check the fallback source is reachable before running wmill
const url = process.env.WINDMILL_ESBUILD_WASM_URL ??
`https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.28.0.tgz`;
const res = await fetch(url, { method: "HEAD" });
if (!res.ok) throw new Error(`esbuild-wasm source unreachable: ${res.status} — set WINDMILL_ESBUILD_WASM_PATH or fix network`);
// or, for offline environments, verify the override:
// fs.existsSync(path.join(process.env.WINDMILL_ESBUILD_WASM_PATH, "lib", "main.js")) Type guard
function isOkResponse(res: Response): boolean {
return res.ok && res.body !== null;
} Try / catch
try {
await getEsbuild();
} catch (e) {
if (String(e).includes("Failed to download esbuild-wasm")) {
// repair native esbuild so the wasm fallback is not needed
console.error("Fix network or set WINDMILL_ESBUILD_WASM_PATH to an extracted esbuild-wasm dir");
}
throw e;
} Prevention
- Keep the native esbuild install healthy (rebuild after node/npm upgrades) so the wasm download path never triggers.
- Set WINDMILL_ESBUILD_WASM_PATH to a vendored esbuild-wasm dir in air-gapped/CI environments.
- Verify WINDMILL_ESBUILD_WASM_URL with a HEAD request before relying on a custom mirror.
- Pre-download and cache the package once on machines with restricted network access.
When it happens
Trigger: Native esbuild fails its transform() smoke test, so the code fetches the tarball from WINDMILL_ESBUILD_WASM_URL or https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-<version>.tgz, and fetch returns res.ok === false or an empty body (HTTP 404, 403, 5xx, or a network error page).
Common situations: Air-gapped or firewalled machines/CI where registry.npmjs.org is unreachable; a corporate proxy blocking the fetch; a custom WINDMILL_ESBUILD_WASM_URL pointing at a missing or wrong-version tarball; a broken npm install (version-mismatched @esbuild/<platform> binary) that triggered the fallback in the first place; npm registry outages or rate limiting.
Related errors
- Failed to read repository archive: {e}
- native esbuild is not usable; falling back to esbuild-wasm (
- ApiError with mapped HTTP status message (e.g. "Not Found",
- Generic Error: status: ${errorStatus}; status text: ${errorS
- Dependency generation failed: ${queueResponse.status} ${queu
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/b251ba942f526068.
Report an issue: GitHub.