halo-dev/halo · error · Error

ESM provider manifest must contain format, entry, and option

Error message

ESM provider manifest must contain format, entry, and optional style only with format "esm".

What it means

PluginUtils.generateFileName throws ServerWebInputException (HTTP 400) when plugin.getSpec().getVersion() is null or blank. The method builds the stored jar name as "<metadata.name>-<version>.jar" and is invoked during install/upgrade copy (PluginServiceImpl.copyToPluginHome) and by PluginReconciler, so a blank version aborts installation and can block reconciliation.

Source

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

  format: "esm";
  entry: string;
  style?: string;
}

export function validateEsmProviderManifest(
  value: unknown
): EsmProviderManifest {
  if (!isRecord(value)) {
    throw new Error("ESM provider manifest must be an object.");
  }
  const keys = Object.keys(value).sort();
  if (
    !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;
}

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Add a valid semantic version to spec.version in plugin.yaml (e.g. version: 1.0.0).
  2. Verify YAML indentation so version sits directly under spec.
  3. Validate the descriptor schema in the plugin build pipeline before publishing the jar.

Example fix

// before
spec:
  displayName: My Plugin
  # version missing
// after
spec:
  displayName: My Plugin
  version: 1.0.0
Defensive patterns

Strategy: validation

Validate before calling

// Reject a blank version before attempting install/reconcile.
String v = plugin.getSpec().getVersion();
if (!StringUtils.hasText(v)) {
    throw new IllegalStateException("plugin.yaml spec.version must be a non-blank semver");
}

Type guard

// Java predicate narrowing a Plugin to 'has a usable version'
static boolean hasValidVersion(Plugin p) {
    return p != null && p.getSpec() != null
        && StringUtils.hasText(p.getSpec().getVersion());
}

Try / catch

Mono.fromCallable(() -> PluginUtils.generateFileName(plugin))
    .onErrorResume(ServerWebInputException.class,
        e -> Mono.error(new BusinessException("Set spec.version in plugin.yaml")))

Prevention

When it happens

Trigger: Installing/upgrading or reconciling a plugin whose plugin.yaml omits spec.version, sets it to an empty string, or places it under the wrong YAML key so it deserializes to null.

Common situations: plugin.yaml missing the version field; wrong indentation putting version outside spec; version set to ""; templating that stripped the value during build.

Related errors


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