{"record":{"id":"1d85bcfd5a4b513c","repo":"halo-dev/halo","slug":"host-runtime-snapshot-must-expose-exactly-share","errorCode":null,"errorMessage":"Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_ROOTS.join(\", \")}.","messagePattern":"Host runtime snapshot must expose exactly: (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ui/packages/ui-plugin-bundler-kit/src/runtime-snapshot.ts","lineNumber":64,"sourceCode":"  if (\n    !isRecord(value) ||\n    typeof value.haloVersion !== \"string\" ||\n    !parseStableVersion(value.haloVersion)\n  ) {\n    throw new Error(\"Host runtime snapshot haloVersion must be stable semver.\");\n  }\n  if (!isRecord(value.packages)) {\n    throw new Error(\"Host runtime snapshot packages must be an object.\");\n  }\n  const snapshotPackages = value.packages;\n\n  const packageRoots = Object.keys(value.packages).sort();\n  const supportedRoots = [...SHARED_PACKAGE_ROOTS].sort();\n  if (\n    packageRoots.length !== supportedRoots.length ||\n    packageRoots.some((root, index) => root !== supportedRoots[index])\n  ) {\n    throw new Error(\n      `Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_ROOTS.join(\", \")}.`\n    );\n  }\n\n  const packages = Object.fromEntries(\n    SHARED_PACKAGE_ROOTS.map((root) => [\n      root,\n      validateSnapshotEntry(root, snapshotPackages[root]),\n    ])\n  ) as Record<SharedPackageRoot, HostRuntimeSnapshotEntry>;\n\n  return deepFreeze({ haloVersion: value.haloVersion, packages });\n}\n\nexport function selectHaloHostRuntimeSnapshot(\n  targetHaloVersion: string,\n  snapshots: readonly HaloHostRuntimeSnapshot[] = HALO_HOST_RUNTIME_SNAPSHOTS\n) {","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/packages/ui-plugin-bundler-kit/src/runtime-snapshot.ts#L46-L82","documentation":"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).","triggerScenarios":"plugin.yaml is empty, contains multiple --- separated documents, or holds a non-Plugin document, so the loaded Unstructured list size is not 1.","commonSituations":"Empty plugin.yaml; concatenating multiple plugin manifests into one file; stray '---' creating extra documents; wrong kind/apiVersion document.","solutions":["Ensure plugin.yaml contains exactly one Plugin document (apiVersion + kind: Plugin).","Remove extra '---' separators and any second document.","Validate that the file is non-empty and parses to a single YAML mapping."],"exampleFix":"// before — two documents\napiVersion: v1alpha1\nkind: Plugin\nmetadata: { name: p1 }\nspec: { version: 1.0.0 }\n---\napiVersion: v1alpha1\nkind: Plugin\nmetadata: { name: p2 }\n// after — single document\napiVersion: v1alpha1\nkind: Plugin\nmetadata: { name: p1 }\nspec: { version: 1.0.0 }","handlingStrategy":"validation","validationCode":"List<Unstructured> docs = new YamlUnstructuredLoader(resource).load();\nif (docs.size() != 1) {\n    throw new IllegalStateException(\"plugin.yaml must contain exactly one Plugin document\");\n}","typeGuard":null,"tryCatchPattern":"pluginService.install(path)\n    .onErrorResume(PluginRuntimeException.class,\n        e -> Mono.error(new BusinessException(\"Malformed plugin.yaml: \" + e.getMessage())))","preventionTips":["Keep plugin.yaml to a single Plugin document.","Remove stray '---' separators.","Lint the YAML in CI."],"tags":["plugin","manifest","install","validation","yaml"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}