{"record":{"id":"b14128c9a4dee4a5","repo":"halo-dev/halo","slug":"esm-provider-manifest-entry-is-required","errorCode":null,"errorMessage":"ESM provider manifest entry is required.","messagePattern":"ESM provider manifest entry is required\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"ui/packages/ui-plugin-bundler-kit/src/provider-manifest.ts","lineNumber":29,"sourceCode":"export function validateEsmProviderManifest(\n  value: unknown\n): EsmProviderManifest {\n  if (!isRecord(value)) {\n    throw new Error(\"ESM provider manifest must be an object.\");\n  }\n  const keys = Object.keys(value).sort();\n  if (\n    !keys.includes(\"entry\") ||\n    !keys.includes(\"format\") ||\n    keys.some((key) => ![\"entry\", \"format\", \"style\"].includes(key)) ||\n    value.format !== \"esm\"\n  ) {\n    throw new Error(\n      'ESM provider manifest must contain format, entry, and optional style only with format \"esm\".'\n    );\n  }\n  if (typeof value.entry !== \"string\") {\n    throw new Error(\"ESM provider manifest entry is required.\");\n  }\n  const manifest: EsmProviderManifest = {\n    format: \"esm\",\n    entry: normalizeProviderResourcePath(value.entry),\n  };\n  if (\"style\" in value) {\n    if (typeof value.style !== \"string\") {\n      throw new Error(\"ESM provider manifest style must be a string.\");\n    }\n    manifest.style = normalizeProviderResourcePath(value.style);\n  }\n  return manifest;\n}\n\nexport function normalizeProviderResourcePath(resourcePath: string) {\n  const normalizedSlashes = resourcePath.replaceAll(\"\\\\\", \"/\");\n  if (\n    !normalizedSlashes ||","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/packages/ui-plugin-bundler-kit/src/provider-manifest.ts#L11-L47","documentation":"Inside UiPluginBundleServiceImpl.readManifest the parsed ui-plugin.json root must be a JSON object; if it is an array, string, number, or scalar this IllegalArgumentException is thrown. It is caught one frame up and converted into ClassifiedProvider.invalid(candidate, message), so the provider is marked invalid rather than crashing the bundle build — the plugin's ESM UI bundle will not be served.","triggerScenarios":"A plugin ships a console/ui-plugin.json whose top-level JSON value is not an object (e.g. a bare array of providers or a string), read when the UI bundle service classifies the provider.","commonSituations":"Author hand-edits ui-plugin.json into an array; build tool emits a JSON array instead of an object; truncation/corruption leaving a scalar.","solutions":["Make ui-plugin.json a single JSON object with format, entry, and optional style fields.","Re-run the plugin scaffold/manifest generator to regenerate a well-formed ui-plugin.json.","Lint the JSON in CI before packaging the plugin jar."],"exampleFix":"// before — ui-plugin.json\n[\"module.js\"]\n// after\n{\n  \"format\": \"esm\",\n  \"entry\": \"module.js\"\n}","handlingStrategy":"validation","validationCode":"// Validate ui-plugin.json shape before packaging the jar.\nJsonNode n = JSON_MAPPER.readTree(uiPluginJsonFile);\nif (!n.isObject()) {\n    throw new IllegalStateException(\"ui-plugin.json must be a JSON object\");\n}","typeGuard":"static boolean isManifestObject(JsonNode n) { return n != null && n.isObject(); }","tryCatchPattern":null,"preventionTips":["Lint ui-plugin.json with a JSON schema in plugin CI.","Generate the manifest with the official scaffold rather than by hand.","Open ui-plugin.json in an editor with JSON validation before publish."],"tags":["plugin","ui-bundle","esm","provider-manifest","validation"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}