{"record":{"id":"7e4a537b45702a2e","repo":"halo-dev/halo","slug":"provider-resource-path-escapes-its-root-resourc","errorCode":null,"errorMessage":"Provider resource path escapes its root: ${resourcePath}.","messagePattern":"Provider resource path escapes its root: (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"ui/packages/ui-plugin-bundler-kit/src/provider-manifest.ts","lineNumber":60,"sourceCode":"}\n\nexport function normalizeProviderResourcePath(resourcePath: string) {\n  const normalizedSlashes = resourcePath.replaceAll(\"\\\\\", \"/\");\n  if (\n    !normalizedSlashes ||\n    normalizedSlashes.startsWith(\"/\") ||\n    normalizedSlashes.startsWith(\"//\") ||\n    /^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(normalizedSlashes) ||\n    normalizedSlashes.includes(\"?\") ||\n    normalizedSlashes.includes(\"#\")\n  ) {\n    throw new Error(\n      `Provider resource path must be provider-root-relative: ${resourcePath}.`\n    );\n  }\n  const normalized = path.posix.normalize(normalizedSlashes);\n  if (normalized === \"..\" || normalized.startsWith(\"../\")) {\n    throw new Error(\n      `Provider resource path escapes its root: ${resourcePath}.`\n    );\n  }\n  return `./${normalized.replace(/^\\.\\//, \"\")}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","sourceCodeStart":42,"sourceCodeEnd":70,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/ui/packages/ui-plugin-bundler-kit/src/provider-manifest.ts#L42-L70","documentation":"validateManifestResource requires the entry (and style) values in ui-plugin.json to be JSON strings. A non-textual node (number, object, array, boolean) throws this IllegalArgumentException, caught upstream and reported as ClassifiedProvider.invalid.","triggerScenarios":"ui-plugin.json's entry or style is a non-string JSON value, e.g. \"entry\": 42 or \"entry\": {\"src\":\"x.js\"}.","commonSituations":"Author wraps the path in an object; build tool writes a numeric token; misconfigured manifest generator.","solutions":["Make entry and style plain string paths in ui-plugin.json.","Point them at the actual file relative to the console/ provider root.","Validate the manifest shape with a JSON schema in the build."],"exampleFix":"// before\n{\n  \"format\": \"esm\",\n  \"entry\": { \"file\": \"index.js\" }\n}\n// after\n{\n  \"format\": \"esm\",\n  \"entry\": \"index.js\"\n}","handlingStrategy":"validation","validationCode":"JsonNode entry = manifest.path(\"entry\");\nif (!entry.isTextual()) {\n    throw new IllegalStateException(\"ui-plugin.json entry must be a string path\");\n}","typeGuard":"static boolean isStringPath(JsonNode n) { return n != null && n.isTextual() && StringUtils.hasText(n.asText()); }","tryCatchPattern":null,"preventionTips":["Keep entry/style as plain string paths.","Do not nest paths inside objects.","Validate types via JSON schema before packaging."],"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"}