halo-dev/halo · warning · Error

ESM provider manifest style must be a string.

Error message

ESM provider manifest style must be a string.

What it means

readManifest requires the ui-plugin.json object to contain exactly format and entry, optionally style, and nothing else. This IllegalArgumentException fires when format or entry is missing, or when an unknown extra field is present. Caught upstream and surfaced as ClassifiedProvider.invalid, degrading the plugin's UI bundle.

Source

Thrown at ui/packages/ui-plugin-bundler-kit/src/provider-manifest.ts:37

    !keys.includes("entry") ||
    !keys.includes("format") ||
    keys.some((key) => !["entry", "format", "style"].includes(key)) ||
    value.format !== "esm"
  ) {
    throw new Error(
      'ESM provider manifest must contain format, entry, and optional style only with format "esm".'
    );
  }
  if (typeof value.entry !== "string") {
    throw new Error("ESM provider manifest entry is required.");
  }
  const manifest: EsmProviderManifest = {
    format: "esm",
    entry: normalizeProviderResourcePath(value.entry),
  };
  if ("style" in value) {
    if (typeof value.style !== "string") {
      throw new Error("ESM provider manifest style must be a string.");
    }
    manifest.style = normalizeProviderResourcePath(value.style);
  }
  return manifest;
}

export function normalizeProviderResourcePath(resourcePath: string) {
  const normalizedSlashes = resourcePath.replaceAll("\\", "/");
  if (
    !normalizedSlashes ||
    normalizedSlashes.startsWith("/") ||
    normalizedSlashes.startsWith("//") ||
    /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(normalizedSlashes) ||
    normalizedSlashes.includes("?") ||
    normalizedSlashes.includes("#")
  ) {
    throw new Error(
      `Provider resource path must be provider-root-relative: ${resourcePath}.`

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Ensure ui-plugin.json has both format and entry; add style only if needed.
  2. Remove any field that is not format, entry, or style.
  3. Diff against a known-good plugin's ui-plugin.json to spot stray keys.

Example fix

// before
{
  "format": "esm",
  "main": "index.js"
}
// after
{
  "format": "esm",
  "entry": "index.js"
}
Defensive patterns

Strategy: validation

Validate before calling

// Enforce the allowed field set before packaging.
Set<String> allowed = Set.of("format", "entry", "style");
Set<String> fields = new HashSet<>(manifest.propertyNames());
if (!fields.containsAll(Set.of("format", "entry")) || !allowed.containsAll(fields)) {
    throw new IllegalStateException("ui-plugin.json must have format+entry, optional style, nothing else");
}

Prevention

When it happens

Trigger: Shipping a console/ui-plugin.json that omits format or entry, or includes unrecognized keys (e.g. a legacy "main" or "version" field).

Common situations: Migrating from an older manifest schema that used different keys; copy-paste leaving a stray field; typos like "entr" instead of "entry".

Related errors


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