quarkusio/quarkus · error · RuntimeException

Failed to create a URL for '<file>'

Error message

Failed to create a URL for '<file>'

What it means

ResourceLoaders.processAsPath resolves a resource URL into a filesystem Path so a consumer function can read it. When the URL points inside a nested JAR, it strips the '!...' suffix and converts the JAR URL to a local Path; if that sub-URL is malformed (cannot be parsed as a URL), a RuntimeException wrapping MalformedURLException is thrown with this message.

Source

Thrown at independent-projects/tools/devtools-common/src/main/java/io/quarkus/platform/descriptor/loader/json/ResourceLoaders.java:60

        if (f.isDirectory()) {
            return new DirectoryResourceLoader(f.toPath());
        }
        if (f.isFile() && FilenameUtils.isExtension(f.getName(), "jar", "zip")) {
            return new ZipResourceLoader(f.toPath());
        }
        throw new IllegalStateException("No compatible ResourceLoader have been found for file type: " + f.getName());
    }

    // This method is copied from io.quarkus.runtime.util.ClassPathUtils
    public static <R> R processAsPath(URL url, Function<Path, R> function) {
        if (JAR.equals(url.getProtocol())) {
            final String file = url.getFile();
            final int exclam = file.lastIndexOf('!');
            final Path jar;
            try {
                jar = toLocalPath(exclam >= 0 ? new URL(file.substring(0, exclam)) : url);
            } catch (MalformedURLException e) {
                throw new RuntimeException("Failed to create a URL for '" + file.substring(0, exclam) + "'", e);
            }
            try (FileSystem jarFs = ZipUtils.newFileSystem(jar)) {
                Path localPath = jarFs.getPath("/");
                if (exclam >= 0) {
                    localPath = localPath.resolve(file.substring(exclam + 1));
                }
                return function.apply(localPath);
            } catch (IOException e) {
                throw new UncheckedIOException("Failed to read " + jar, e);
            }
        }

        if (FILE.equals(url.getProtocol())) {
            return function.apply(toLocalPath(url));
        }

        throw new IllegalArgumentException("Unexpected protocol " + url.getProtocol() + " for URL " + url);
    }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Print the offending URL and check the text before the last '!' is a fully-formed URL with scheme (e.g. file:/path/to.jar)
  2. Fix the code that constructed the URL so the JAR part includes its scheme (prepend 'file:' if it came from a raw path)
  3. If the resource is on the local filesystem, ensure it is passed as a 'file:' URL so the FILE protocol branch is used instead of the nested-JAR branch

Example fix

// before
URL url = new URL("/opt/app/lib/quarkus.jar!/quarkus-extension.yaml"); // MalformedURLException downstream
// after
URL url = new URL("file:/opt/app/lib/quarkus.jar!/quarkus-extension.yaml");
Defensive patterns

Strategy: validation

Validate before calling

static boolean safeNestedJarUrl(URL url) {
    String f = url.getFile();
    int exclam = f.lastIndexOf('!');
    if (exclam < 0) return true;
    try { new URL(f.substring(0, exclam)); return true; }
    catch (MalformedURLException e) { return false; }
}

Type guard

static boolean hasJarScheme(URL url) {
    String f = url.getFile();
    int i = f.lastIndexOf('!');
    return i < 0 || f.startsWith("file:", 0) && i > "file:".length();
}

Try / catch

try {
    ResourceLoaders.processAsPath(url, fn);
} catch (RuntimeException e) {
    if (e.getCause() instanceof MalformedURLException) {
        throw new IllegalStateException("Malformed nested-jar URL: " + url, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling loadResourceAsPath/processAsPath with a URL whose file portion contains a '!' but whose part before the last '!' is not a valid URL (e.g. a plain path 'file1.jar' with no scheme, or an improperly encoded URL string).

Common situations: Spring Boot-style fat/nested JARs (jar:file:...!/...!/...) where the nested entry is extracted incorrectly; custom classloaders constructing non-standard 'jar:' URLs; corrupted MANIFEST classpath entries with malformed jar paths.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/381f7c3b0192072b. Report an issue: GitHub.