{"record":{"id":"93ea34cd5cd06555","repo":"halo-dev/halo","slug":"host-runtime-snapshot-haloversion-must-be-stable-s","errorCode":null,"errorMessage":"Host runtime snapshot haloVersion must be stable semver.","messagePattern":"Host runtime snapshot haloVersion must be stable semver\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ui/packages/ui-plugin-bundler-kit/src/runtime-snapshot.ts","lineNumber":51,"sourceCode":"  haloVersion: string;\n  packages: Record<SharedPackageRoot, HostRuntimeSnapshotEntry>;\n}\n\nconst sharedPackageRootSet = new Set<string>(SHARED_PACKAGE_ROOTS);\n\nexport const HALO_HOST_RUNTIME_SNAPSHOTS = Object.freeze(\n  rawHaloHostRuntimeSnapshots.map(validateHaloHostRuntimeSnapshot)\n);\n\nexport function validateHaloHostRuntimeSnapshot(\n  value: unknown\n): HaloHostRuntimeSnapshot {\n  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(","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/packages/ui-plugin-bundler-kit/src/runtime-snapshot.ts#L33-L69","documentation":"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.","triggerScenarios":"Installing, upgrading, or reloading a plugin jar that contains no discoverable plugin.yaml entry, when getPath returns null instead of throwing.","commonSituations":"Corrupt or empty jar; descriptor stored under an unexpected archive path; jar built without including plugin.yaml.","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)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before install, confirm the jar contains a discoverable descriptor.\ntry (JarFile jar = new JarFile(jarPath.toFile())) {\n    if (jar.getEntry(\"plugin.yaml\") == null) {\n        throw new IllegalStateException(\"jar has no plugin.yaml\");\n    }\n}","typeGuard":null,"tryCatchPattern":"pluginService.install(path)\n    .onErrorResume(PluginRuntimeException.class,\n        e -> Mono.error(new BusinessException(\"Invalid plugin package: \" + e.getMessage())))","preventionTips":["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."],"tags":["plugin","manifest","install","packaging","pf4j"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}