{"record":{"id":"9de040ef99c23528","repo":"windmill-labs/windmill","slug":"native-esbuild-is-not-usable-falling-back-to-esbu","errorCode":null,"errorMessage":"native esbuild is not usable; falling back to esbuild-wasm (${msg.trim()})","messagePattern":"native esbuild is not usable; falling back to esbuild-wasm \\((.+?)\\)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cli/src/utils/esbuild_loader.ts","lineNumber":73,"sourceCode":"async function acquireEsbuild(): Promise<Esbuild> {\n  // Escape hatch: skip native entirely (e.g. a host known to have a broken\n  // install, or to exercise the fallback path).\n  if (process.env.WINDMILL_FORCE_ESBUILD_WASM) {\n    return loadWasmEsbuild(await nativeHostVersion());\n  }\n\n  try {\n    const esbuild = await import(\"esbuild\");\n    // The native service only starts on the first call; force it with the most\n    // trivial op so any breakage (host/binary version mismatch, a dead service)\n    // surfaces now rather than mid-build. The mismatch detail is printed to the\n    // child's stderr while the thrown error is generic (\"service was stopped\"),\n    // so we fall back on ANY smoke-test failure rather than matching a string.\n    await esbuild.transform(\"\");\n    return esbuild;\n  } catch (e) {\n    const msg = e instanceof Error ? e.message : String(e);\n    log.warn(\n      `native esbuild is not usable; falling back to esbuild-wasm (${msg.trim()})`\n    );\n  }\n\n  return loadWasmEsbuild(await nativeHostVersion());\n}\n\n/**\n * Stops the esbuild service (native or wasm — both spawn a child process) so the\n * process can exit. Safe to call repeatedly; the service restarts lazily on the\n * next build.\n */\nexport async function stopEsbuild(): Promise<void> {\n  await cached?.stop();\n}\n\nasync function nativeHostVersion(): Promise<string> {\n  try {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/cli/src/utils/esbuild_loader.ts#L55-L91","documentation":"acquireEsbuild loads the native esbuild package and runs a smoke test (esbuild.transform(\"\")) because the native service only starts on first use — a broken install (e.g. host/binary version mismatch after a partial npm install, or a dead service) only surfaces then, and the thrown error is generic. On ANY smoke-test failure it logs this warning with the underlying message and falls back to esbuild-wasm, downloading and caching it if needed. The CLI still works, just slower.","triggerScenarios":"First esbuild use in the CLI (app/dependency builds) when the native esbuild import or its smoke-test transform throws: @esbuild/<platform> binary missing or version-mismatched with the JS host, corrupted node_modules, unsupported platform, or the spawned esbuild service dying immediately.","commonSituations":"Incremental/partial `npm install` leaving esbuild host at a different version than the native binary (\"Cannot start service: Host version X does not match binary version Y\"); running the CLI on an unusual platform where the binary wasn't installed; AV software deleting the native binary; followed by a 14MB esbuild-wasm download on first fallback.","solutions":["Reinstall dependencies cleanly: rm -rf node_modules package-lock.json && npm install (or bun install) to realign esbuild host and native binary versions.","Verify the native binary: npx esbuild --version — if it errors, the install is broken.","If the platform genuinely lacks native esbuild, accept the wasm fallback; set WINDMILL_ESBUILD_WASM_PATH to a pre-extracted esbuild-wasm dir for air-gapped machines.","Set WINDMILL_FORCE_ESBUILD_WASM=1 to intentionally skip native and skip the warning path's failed probe.","Check the CLI package integrity (reinstall wmill) if only its bundled esbuild is broken."],"exampleFix":"// before\nwmill app build ...\n// native esbuild is not usable; falling back to esbuild-wasm (Cannot start service: Host version ...)\n\n// after: repair the install\nrm -rf node_modules\nnpm ci\nwmill app build ...   # no fallback warning","handlingStrategy":"fallback","validationCode":"try {\n  const esbuild = await import(\"esbuild\");\n  await esbuild.transform(\"\");\n  console.log(\"native esbuild OK\");\n} catch (e) {\n  console.log(\"native esbuild broken — reinstall deps or pre-seed WINDMILL_ESBUILD_WASM_PATH:\", e);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await $`wmill app build ...`;\n} catch (e) {\n  // fallback usually succeeds; only fail on real errors, e.g. wasm download failure\n  if (String(e).includes(\"Failed to download esbuild-wasm\")) {\n    process.env.WINDMILL_ESBUILD_WASM_PATH = \"/opt/vendored/esbuild-wasm\";\n    await $`wmill app build ...`;\n  } else throw e;\n}","preventionTips":["Use npm ci / lockfiles so esbuild host and @esbuild/<platform> binary versions always match","Set WINDMILL_ESBUILD_WASM_PATH (or WINDMILL_ESBUILD_WASM_URL) in air-gapped environments so the fallback needs no download","Verify installs with `npx esbuild --version` in CI setup","Set WINDMILL_FORCE_ESBUILD_WASM=1 deliberately on platforms known to break native esbuild","Check that endpoint protection isn't quarantining the native esbuild binary"],"tags":["cli","esbuild","fallback","native-binary","dependency-corruption"],"backgroundTag":"esbuild-version-mismatch","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}