Troubleshooting
Known failure modes with concrete fixes — install errors, bundle config, import queries, module workers, out-of-root imports, and missing esbuild binaries.
What this page covers
A symptom-first index of failure modes that come up often enough to be worth naming directly, each with the exact message you'll see and the fix. If your problem isn't here, the linked concept/guide pages cover the full behavior each feature is built on.
curl | sh install fails to resolve a release tag
Symptom: the curl installer exits with one of:
error: could not resolve the latest release tag from GitHub APIerror: could not resolve the latest prerelease tag from GitHub API (no prerelease found in the last 30 releases)Cause: install.sh resolves a tag from the GitHub Releases API before downloading anything.
With
ZFB_VERSIONunset, it asks for the latest non-prerelease release. This normally resolves; it fails only if the API is unreachable, rate-limited, or the request is otherwise blocked. See Install without Node.With
ZFB_VERSION=latest-prerelease, it scans the most recent 30 releases for one flagged"prerelease": true. If none of the last 30 is a prerelease (for example, several stable releases have since shipped and prereleases have rolled off that window), resolution fails the same way.
Fix:
If you are rate-limited or behind a proxy: pin an exact tag from the releases page, e.g.
ZFB_VERSION=v1.0.0, which skips the tag-resolution request entirely.If you specifically want a prerelease and
latest-prereleasefails to resolve (none of the last 30 releases is a prerelease): pin the exact prerelease tag instead, e.g.ZFB_VERSION=v0.1.0-next.99.
musl/Alpine is not supported yet
Symptom:
[zfb] musl/Alpine is not supported yet — no @takazudo/zfb-linux-*-musl package exists.
Use a glibc-based Linux image (e.g. Debian/Ubuntu) instead of Alpine. Cause: zfb ships prebuilt, glibc-linked platform binaries only — no @takazudo/zfb-linux-*-musl package exists. The npm wrapper (packages/) detects a musl runtime (Alpine's / dynamic loader, or the absence of a glibc version in Node's own runtime report) and fails with this friendly message instead of letting the glibc binary crash with an opaque dynamic-loader error. The standalone curl/PowerShell installers can't detect musl this way (they only check OS/CPU) and will download a glibc binary that then fails to execute outright.
Fix: use a glibc-based Linux base image (Debian, Ubuntu) instead of Alpine — in a Dockerfile, a CI runner, or any container image. There is no workaround that runs zfb natively on musl today; see Installation and Install without Node for the supported platform matrix.
import.meta.glob(...) build error
Symptom, from zfb build (or a warning + skipped rebundle from zfb dev):
zfb islands: `import.meta.glob(...)` cannot be safely shipped from one or more
files reachable from a "use client" island. zfb can currently expand eager
string-literal globs only when the glob call is in an island-reachable module
that is written as a real shadow copy; unsupported forms and globs found only in
raw-mirrored glob target/subtree files would ship to the browser unexpanded and
throw at hydration: <file> — ... Follow the remediation above for each file,
use the eager string-literal form where applicable, replace the glob with
explicit static imports, or move the usage to a server-only (non-"use client")
module. Tracked at https://github.com/Takazudo/zudo-front-builder/issues/1385
and https://github.com/Takazudo/zudo-front-builder/issues/1412. Cause: import.meta.glob is a Vite-only build-time macro; esbuild (which bundles islands) has no native understanding of it, so zfb expands the one supported shape itself before esbuild ever sees the file. Only import.meta.glob("<string-literal>", { eager: true }), with the pattern resolving under the importing file's own directory, is supported when the glob call is in an island-reachable module that zfb writes as a real expanded shadow-copy file. Everything else — the default lazy form, { eager: false }, a non-literal pattern, the import/query/as options, or a .-escaping pattern — is rejected. zfb also rejects a glob inside a JS-like file that is reachable only as a raw-mirrored glob target or subtree companion, because that file would otherwise ship to the browser unexpanded and throw during hydration.
Fix: see Islands: import.meta.glob support for the full support matrix and alternatives. Use the eager string-literal form where applicable, hoist nested globs into island-reachable modules, move raw-mirrored glob target/subtree files out of the globbed subtree, or replace the glob with explicit static imports. Note that client scripts (*.client.ts) don't support import.meta.glob at all, and — unlike islands — that failure is not caught at build time; see Client Scripts: import.meta.glob is not supported.
bundle.loaders or bundle.define is rejected
Symptom: config loading fails with a message such as:
bundle.loaders key ".asset" uses unsupported loader "file"; inline-only v1 accepts: ...
bundle.define key "import.meta.env.DEV" is reserved by zfb's bundle mode ...An invalid raw define expression can instead surface as an esbuild parse error when the first affected bundle runs.
Cause: loader keys must start with ., use one of text, json, base64, dataurl, binary, or empty, and cannot override .css, .module.css, .mdx, or .md. The asset-emitting file and copy loaders are not supported. zfb also owns import.meta.env.DEV, import.meta.env.PROD, and process.env.NODE_ENV, so user defines cannot replace them. Define values are raw esbuild expressions, not automatically quoted strings.
Fix: choose an inline loader and an unreserved extension/key. For a string define, include JSON quotes in the replacement (for example, __APP_NAME__: '"my-app"'). See defineConfig: Bundle settings for the full contract and the browser-exposure warning.
A ?raw import fails
Symptom: the build, or a browser rebundle during dev, reports unsupported import query form, does not resolve to an existing file, or is not valid UTF-8 text.
Cause: zfb supports exactly a static default import with a literal, project-local relative target and the exact ?raw suffix. The target is a terminal UTF-8 text file. Dynamic, named, namespace, side-effect, type-only, or re-export forms; ?url; extra query parameters; non-literal paths; out-of-root/symlink escapes; and non-UTF-8 targets are rejected.
Fix: reduce the import to this shape and make sure the exact file exists inside the project root:
import text from "./file.ext?raw";The extension can be anything and does not need a bundle.loaders entry. See Islands: Importing text with ?raw or Client Scripts.
A module worker is not emitted, or SharedWorker fails
Symptom: a worker constructor remains unchanged and no worker-*.js companion appears, or the build reports unsupported SharedWorker.
Cause: zfb discovers the unshadowed global Worker only in the literal module form below. The URL must point to an exact project-local relative JS/TS file without a query or fragment. Non-literal and classic-worker shapes are outside the contract. Discovery deliberately does not traverse installed node_modules. A SharedWorker using the same literal URL shape is rejected loudly because leaving that apparently supported URL unrewritten would make it 404 at runtime.
Fix: for a first-party dedicated worker, use:
const worker = new Worker(
new URL("./worker.ts", import.meta.url),
{ type: "module" },
);Island workers appear under /; client-script workers use /. The rewritten URL keeps that stable name and adds an exactly eight-character lowercase hexadecimal ?v= graph hash. For unsupported shapes or third-party transitive workers, bundle them with package-specific tooling. See Islands: Module workers.
A relative import "Could not resolve" outside the project root
Symptom: zfb build (or zfb dev) fails with an esbuild Could not resolve ". error, and the accompanying zfb note explains a "shadow-copy build boundary."
Cause: zfb builds by shadow-copying the project root into a temporary directory before running esbuild. A relative import that walks above the project root (e.g. .) resolves outside that shadow copy and cannot be found. This is diagnose-only, not a resolvable bug — the underlying escape stays unsupported; zfb only makes the reported error name the real (non-shadow) path and explain the workaround instead of leaking a meaningless temp-directory path.
Fix: expose the out-of-root target as a package import instead of a relative one — add a wildcard exports entry to the target package's package.json (e.g. "./src/*": "./src/*") and import it by package specifier (e.g. @scope/). node_modules (including file:/workspace-linked packages) IS included in the shadow copy, so package-specifier imports resolve normally. Full details: defineConfig: Watching paths outside the project root; tracked at issue #1385.
esbuild binary not found from an embedded zfb-server
Symptom: a Rust host embedding zfb-server (see Embed as library) fails with an "esbuild binary not found" error when config_path points at a zfb.config.ts file.
Cause: evaluating a TypeScript config first bundles it with esbuild, and an embedded zfb-server does not ship its own esbuild binary the way the zfb CLI does.
Fix: either switch config_path to a zfb.config.json file (no esbuild needed at all — the simplest fix), or set the ZFB_ESBUILD_BIN environment variable to a usable esbuild CLI binary before calling build(). See Embed as library: config format for the full explanation.