halo-dev/halo · error · Error

Host runtime snapshot must expose exactly

Error message

Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_ROOTS.join(", ")}.

What it means

YamlPluginFinder.unstructuredToPlugin loads the descriptor YAML and requires exactly one YAML document; if the loader returns zero or more than one Unstructured, this PluginRuntimeException is thrown (the file name placeholder is the configured propertiesFileName).

Solutions

  1. Ensure plugin.yaml contains exactly one Plugin document (apiVersion + kind: Plugin).
  2. Remove extra '---' separators and any second document.
  3. Validate that the file is non-empty and parses to a single YAML mapping.

Example fix

// before — two documents
apiVersion: v1alpha1
kind: Plugin
metadata: { name: p1 }
spec: { version: 1.0.0 }
---
apiVersion: v1alpha1
kind: Plugin
metadata: { name: p2 }
// after — single document
apiVersion: v1alpha1
kind: Plugin
metadata: { name: p1 }
spec: { version: 1.0.0 }
Defensive patterns

Strategy: validation

Validate before calling

List<Unstructured> docs = new YamlUnstructuredLoader(resource).load();
if (docs.size() != 1) {
    throw new IllegalStateException("plugin.yaml must contain exactly one Plugin document");
}

Try / catch

pluginService.install(path)
    .onErrorResume(PluginRuntimeException.class,
        e -> Mono.error(new BusinessException("Malformed plugin.yaml: " + e.getMessage())))

Prevention

When it happens

Trigger: plugin.yaml is empty, contains multiple --- separated documents, or holds a non-Plugin document, so the loaded Unstructured list size is not 1.

Common situations: Empty plugin.yaml; concatenating multiple plugin manifests into one file; stray '---' creating extra documents; wrong kind/apiVersion document.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/1d85bcfd5a4b513c. Report an issue: GitHub.

Appendix: source

Thrown at ui/packages/ui-plugin-bundler-kit/src/runtime-snapshot.ts:64

  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(
    SHARED_PACKAGE_ROOTS.map((root) => [
      root,
      validateSnapshotEntry(root, snapshotPackages[root]),
    ])
  ) as Record<SharedPackageRoot, HostRuntimeSnapshotEntry>;

  return deepFreeze({ haloVersion: value.haloVersion, packages });
}

export function selectHaloHostRuntimeSnapshot(
  targetHaloVersion: string,
  snapshots: readonly HaloHostRuntimeSnapshot[] = HALO_HOST_RUNTIME_SNAPSHOTS
) {

View on GitHub (pinned to d2f5165f9c)