halo-dev/halo · error · Error
Host runtime snapshot haloVersion must be stable semver.
Error message
Host runtime snapshot haloVersion must be stable semver.
What it means
YamlPluginFinder.readPluginDescriptor throws this pf4j PluginRuntimeException when getManifestPath returns null — which for a jar happens when FileUtils.getPath cannot locate the descriptor entry. It surfaces through findPluginManifest during install/upgrade/reload, failing the operation.
Source
Thrown at ui/packages/ui-plugin-bundler-kit/src/runtime-snapshot.ts:51
haloVersion: string;
packages: Record<SharedPackageRoot, HostRuntimeSnapshotEntry>;
}
const sharedPackageRootSet = new Set<string>(SHARED_PACKAGE_ROOTS);
export const HALO_HOST_RUNTIME_SNAPSHOTS = Object.freeze(
rawHaloHostRuntimeSnapshots.map(validateHaloHostRuntimeSnapshot)
);
export function validateHaloHostRuntimeSnapshot(
value: unknown
): HaloHostRuntimeSnapshot {
if (
!isRecord(value) ||
typeof value.haloVersion !== "string" ||
!parseStableVersion(value.haloVersion)
) {
throw new Error("Host runtime snapshot haloVersion must be stable semver.");
}
if (!isRecord(value.packages)) {
throw new Error("Host runtime snapshot packages must be an object.");
}
const snapshotPackages = value.packages;
const packageRoots = Object.keys(value.packages).sort();
const supportedRoots = [...SHARED_PACKAGE_ROOTS].sort();
if (
packageRoots.length !== supportedRoots.length ||
packageRoots.some((root, index) => root !== supportedRoots[index])
) {
throw new Error(
`Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_ROOTS.join(", ")}.`
);
}
const packages = Object.fromEntries(View on GitHub (pinned to d2f5165f9c)
Solutions
- Open the jar (`jar tf plugin.jar`) and confirm plugin.yaml is present at the expected location.
- Rebuild the plugin ensuring plugin.yaml is packaged at the archive root (or the configured classes directory).
- If installing from a directory, ensure plugin.yaml sits under a classes directory (see error 116).
Defensive patterns
Strategy: validation
Validate before calling
// Before install, confirm the jar contains a discoverable descriptor.
try (JarFile jar = new JarFile(jarPath.toFile())) {
if (jar.getEntry("plugin.yaml") == null) {
throw new IllegalStateException("jar has no plugin.yaml");
}
} Try / catch
pluginService.install(path)
.onErrorResume(PluginRuntimeException.class,
e -> Mono.error(new BusinessException("Invalid plugin package: " + e.getMessage()))) Prevention
- Validate the jar contains plugin.yaml in CI before publishing.
- Use the official plugin build, which stages the descriptor correctly.
- Don't hand-zip plugins.
When it happens
Trigger: Installing, upgrading, or reloading a plugin jar that contains no discoverable plugin.yaml entry, when getPath returns null instead of throwing.
Common situations: Corrupt or empty jar; descriptor stored under an unexpected archive path; jar built without including plugin.yaml.
Related errors
- Invalid target Halo version: ${targetHaloVersion}.
- Invalid attachment
- ESM provider manifest must contain format, entry, and option
- Host runtime snapshot packages must be an object.
- Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/93ea34cd5cd06555.
Report an issue: GitHub.