halo-dev/halo · error · Error
ESM UI provider output must contain one entry with a default
Error message
ESM UI provider output must contain one entry with a default PluginModule export.
What it means
Thrown in the generateBundle hook of the halo:esm-ui-provider Vite plugin when the ESM output does not contain exactly one entry chunk that exports 'default'. The ESM provider contract requires a single entry module with `export default definePluginModule(...)` (a PluginModule) so the host can discover and activate it; zero entries, multiple entries, or a missing default export all violate the contract.
Source
Thrown at ui/packages/ui-plugin-bundler-kit/src/vite-esm.ts:46
},
async transform(code, id) {
if (!id.includes("\0")) {
await validator.validateSource(code, id);
}
},
generateBundle: {
order: "post",
async handler(_outputOptions, bundle) {
const chunks = Object.values(bundle).filter(
(item) => item.type === "chunk"
);
for (const chunk of chunks) {
await validator.validateSource(chunk.code, chunk.fileName);
}
const entries = chunks.filter((chunk) => chunk.isEntry);
if (entries.length !== 1 || !entries[0].exports.includes("default")) {
throw new Error(
"ESM UI provider output must contain one entry with a default PluginModule export."
);
}
const entryStyles = [
...((entries[0] as ViteOutputChunkMetadata).viteMetadata
?.importedCss || []),
].sort();
if (entryStyles.length > 1) {
throw new Error(
"ESM UI provider output must contain at most one entry stylesheet."
);
}
const manifest = validateEsmProviderManifest({
format: "esm",
entry: `./${entries[0].fileName}`,
...(entryStyles[0] ? { style: `./${entryStyles[0]}` } : {}),
});
this.emitFile({View on GitHub (pinned to d2f5165f9c)
Solutions
- Ensure the entry file has exactly `export default definePluginModule({ ... })` as its default export.
- Configure rollupOptions.input (or build.lib) to a single entry file for the ESM build.
- Grep the entry for 'export default' and confirm it is the PluginModule; remove any secondary entry inputs from the ESM build config.
Example fix
// before (entry.ts)
export const plugin = definePluginModule({ routes: [] });
// after
export default definePluginModule({ routes: [] }); Defensive patterns
Strategy: validation
Validate before calling
// Pre-build check: ensure the entry file has a single default PluginModule export.
import { readFileSync } from "node:fs";
const entry = readFileSync("src/index.ts", "utf8");
if (!/export\s+default\s+definePluginModule/.test(entry)) {
throw new Error("Entry must `export default definePluginModule(...)`.");
}
// And in vite.config ensure a single input for the esm build. Type guard
function hasSingleDefaultEntry<T extends { isEntry: boolean; exports: readonly string[] }>(chunks: T[]): boolean {
const entries = chunks.filter((c) => c.isEntry);
return entries.length === 1 && entries[0].exports.includes("default");
} Prevention
- Keep exactly one entry input for the ESM build (build.lib or rollupOptions.input).
- Make the entry file's last statement `export default definePluginModule(...)`.
- Add a CI grep asserting the default export before building.
When it happens
Trigger: Build output has 0 entry chunks (e.g. build.lib misconfigured, or all chunks are non-entry), 2+ entry chunks (multiple inputs), or exactly one entry whose exports array lacks 'default' (the entry uses named exports only, or exports nothing).
Common situations: Vite build.rollupOptions.input lists more than one entry; the entry file calls definePluginModule but assigns it to a const without a default export; a tool/plugin strips the default export; entry is a CSS/asset-only chunk.
Related errors
- ${root} resolved to invalid version ${resolved.version} at $
- ${root} snapshot version must be stable semver.
- ${root} snapshot exports must be unique identifiers.
- ${root} snapshot runtime descriptor is invalid.
- Unsupported shared dependency subpath ${specifier} imported
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/355a859a7e32fd8b.
Report an issue: GitHub.