halo-dev/halo · error · Error
ESM provider manifest must contain format, entry, and option
Error message
ESM provider manifest must contain format, entry, and optional style only with format "esm".
What it means
PluginUtils.generateFileName throws ServerWebInputException (HTTP 400) when plugin.getSpec().getVersion() is null or blank. The method builds the stored jar name as "<metadata.name>-<version>.jar" and is invoked during install/upgrade copy (PluginServiceImpl.copyToPluginHome) and by PluginReconciler, so a blank version aborts installation and can block reconciliation.
Source
Thrown at ui/packages/ui-plugin-bundler-kit/src/provider-manifest.ts:24
format: "esm";
entry: string;
style?: string;
}
export function validateEsmProviderManifest(
value: unknown
): EsmProviderManifest {
if (!isRecord(value)) {
throw new Error("ESM provider manifest must be an object.");
}
const keys = Object.keys(value).sort();
if (
!keys.includes("entry") ||
!keys.includes("format") ||
keys.some((key) => !["entry", "format", "style"].includes(key)) ||
value.format !== "esm"
) {
throw new Error(
'ESM provider manifest must contain format, entry, and optional style only with format "esm".'
);
}
if (typeof value.entry !== "string") {
throw new Error("ESM provider manifest entry is required.");
}
const manifest: EsmProviderManifest = {
format: "esm",
entry: normalizeProviderResourcePath(value.entry),
};
if ("style" in value) {
if (typeof value.style !== "string") {
throw new Error("ESM provider manifest style must be a string.");
}
manifest.style = normalizeProviderResourcePath(value.style);
}
return manifest;
}View on GitHub (pinned to d2f5165f9c)
Solutions
- Add a valid semantic version to spec.version in plugin.yaml (e.g. version: 1.0.0).
- Verify YAML indentation so version sits directly under spec.
- Validate the descriptor schema in the plugin build pipeline before publishing the jar.
Example fix
// before spec: displayName: My Plugin # version missing // after spec: displayName: My Plugin version: 1.0.0
Defensive patterns
Strategy: validation
Validate before calling
// Reject a blank version before attempting install/reconcile.
String v = plugin.getSpec().getVersion();
if (!StringUtils.hasText(v)) {
throw new IllegalStateException("plugin.yaml spec.version must be a non-blank semver");
} Type guard
// Java predicate narrowing a Plugin to 'has a usable version'
static boolean hasValidVersion(Plugin p) {
return p != null && p.getSpec() != null
&& StringUtils.hasText(p.getSpec().getVersion());
} Try / catch
Mono.fromCallable(() -> PluginUtils.generateFileName(plugin))
.onErrorResume(ServerWebInputException.class,
e -> Mono.error(new BusinessException("Set spec.version in plugin.yaml"))) Prevention
- Always set spec.version in plugin.yaml scaffolds.
- Add a schema/required-field check to the plugin build.
- Treat a blank version as a build failure, not a runtime surprise.
When it happens
Trigger: Installing/upgrading or reconciling a plugin whose plugin.yaml omits spec.version, sets it to an empty string, or places it under the wrong YAML key so it deserializes to null.
Common situations: plugin.yaml missing the version field; wrong indentation putting version outside spec; version set to ""; templating that stripped the value during build.
Related errors
- User permissions not set in PermissionUtils
- ESM provider manifest must be an object.
- Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_
- Invalid attachment
- Host runtime snapshot haloVersion must be stable semver.
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/48d1c8d7bf2bbd4f.
Report an issue: GitHub.