quarkusio/quarkus · error · IllegalArgumentException

%s is not a valid resource name as it doesn't end with .clas

Error message

%s is not a valid resource name as it doesn't end with .class

What it means

ClassLoaderHelper.fromResourceNameToClassName() converts a class-file resource name (e.g. 'com/acme/Foo.class') into a class name ('com.acme.Foo') and refuses any resource that doesn't end with the '.class' suffix, throwing IllegalArgumentException. This guards against silently producing garbage class names from non-class resources.

Source

Thrown at independent-projects/bootstrap/classloader-commons/src/main/java/io/quarkus/commons/classloading/ClassLoaderHelper.java:36

     *
     * @param className
     * @return the name of the respective resource
     */
    public static String fromClassNameToResourceName(final String className) {
        //Important: avoid indy!
        return className.replace('.', '/').concat(CLASS_SUFFIX);
    }

    /**
     * Helper method to convert a resource name into the corresponding class name:
     * replace all "/" with "." and remove the ".class" postfix.
     *
     * @param resourceName
     * @return the name of the respective class
     */
    public static String fromResourceNameToClassName(final String resourceName) {
        if (!resourceName.endsWith(CLASS_SUFFIX)) {
            throw new IllegalArgumentException(
                    String.format("%s is not a valid resource name as it doesn't end with .class", resourceName));
        }

        return resourceName.substring(0, resourceName.length() - CLASS_SUFFIX.length()).replace('/', '.');
    }

    public static boolean isInJdkPackage(String name) {
        return name.startsWith(JAVA) || name.startsWith(JDK_INTERNAL) || name.startsWith(SUN_MISC);
    }

    /**
     * Returns {@code true} if the resource name represents a regular class file,
     * excluding {@code module-info.class} and {@code package-info.class}.
     *
     * @param resourceName the JAR entry path, e.g. {@code com/example/Foo.class}
     */
    public static boolean isClassEntry(String resourceName) {
        return resourceName.endsWith(CLASS_SUFFIX)

View on GitHub (pinned to e1c734241f)

Solutions

  1. Filter resource names with name.endsWith(".class") before calling the method
  2. Strip query/trailing characters (like '/' or '!/...') from resource URLs so the name really ends in '.class'
  3. If the resource is not a class, do not convert it — handle it in a separate code path

Example fix

// before
String cn = ClassLoaderHelper.fromResourceNameToClassName("META-INF/beans.xml");
// after
if (resource.endsWith(".class")) {
    String cn = ClassLoaderHelper.fromResourceNameToClassName(resource);
}
Defensive patterns

Strategy: validation

Validate before calling

String toClassName(String resource) {
    if (!resource.endsWith(".class")) {
        return null; // not a class resource; skip
    }
    return ClassLoaderHelper.fromResourceNameToClassName(resource);
}

Type guard

boolean isClassResource(String name) {
    return name != null && name.endsWith(".class") && !name.endsWith(".class/");
}

Try / catch

try {
    return ClassLoaderHelper.fromResourceNameToClassName(resourceName);
} catch (IllegalArgumentException e) {
    if (e.getMessage().endsWith("doesn't end with .class")) {
        log.debugf("Skipping non-class resource: %s", resourceName);
        return null;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling ClassLoaderHelper.fromResourceNameToClassName() with paths like 'com/acme/Foo', 'META-INF/beans.xml', 'com/acme/Foo.class/', or any resource not ending exactly in '.class'.

Common situations: Scanning classloader resources and passing every entry to the converter without filtering by suffix; index entries for nested class sources or generated resources mixed in with class files; off-by-one slicing that dropped '.class' before the call.

Related errors


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