halo-dev/halo · error · IllegalArgumentException

Provider resource path escapes its root: {resourcePath}

Error message

Provider resource path escapes its root: {resourcePath}

What it means

Error "Provider resource path escapes its root: {resourcePath}" thrown in halo-dev/halo.

Source

Thrown at application/src/main/java/run/halo/app/plugin/UiPluginBundleServiceImpl.java:263

    private static String normalizeResourcePath(String resourcePath) {
        if (!StringUtils.hasText(resourcePath)) {
            throw new IllegalArgumentException("Provider resource path must not be blank");
        }
        var normalizedSlashes = resourcePath.replace('\\', '/');
        if (normalizedSlashes.startsWith("/")
                || normalizedSlashes.startsWith("//")
                || normalizedSlashes.contains("?")
                || normalizedSlashes.contains("#")
                || normalizedSlashes.matches("^[a-zA-Z][a-zA-Z0-9+.-]*:.*")) {
            throw new IllegalArgumentException(
                    "Provider resource path must be provider-root-relative: " + resourcePath);
        }
        var normalized = Path.of(normalizedSlashes).normalize();
        if (normalized.isAbsolute()
                || normalized.startsWith("..")
                || normalized.toString().equals(".")) {
            throw new IllegalArgumentException("Provider resource path escapes its root: " + resourcePath);
        }
        return normalized.toString().replace('\\', '/');
    }

    private UiPluginProviderDescriptor createDescriptor(String version, List<KeyedProvider> keyedProviders) {
        var hasLegacyProvider = keyedProviders.stream()
                .map(KeyedProvider::provider)
                .anyMatch(provider -> provider.kind() == ProviderKind.LEGACY);
        return new UiPluginProviderDescriptor(
                keyedProviders.stream().map(this::describeProvider).toList(),
                hasLegacyProvider ? versionedBundleUrl("bundle.js", version) : null);
    }

    private UiPluginProviderDescriptor.Provider describeProvider(KeyedProvider keyedProvider) {
        var provider = keyedProvider.provider();
        var candidate = provider.candidate();
        var cacheKey = keyedProvider.cacheKey();
        return switch (provider.kind()) {

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Remove '..' segments from the manifest resource path so it stays inside the provider root.
  2. Rebuild the plugin bundle; path traversal in the manifest usually comes from a misconfigured output path.
  3. Confirm the entry file is packaged under the provider root directory, not referenced from outside it.

When it happens

Trigger: Thrown at application/src/main/java/run/halo/app/plugin/UiPluginBundleServiceImpl.java:263 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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