apache/dubbo · error · IllegalArgumentException

{} is not visible from class loader

Error message

{} is not visible from class loader

What it means

Thrown by Proxy.buildInterfacesKey when an interface class cannot be re-resolved through the ClassLoader of ics[0] (Class.forName(itf, false, cl) returns a different Class object or null). This is the classic multi-classloader visibility problem: the interface was loaded by one loader but is invisible to the loader that will define the proxy.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/bytecode/Proxy.java:110

        return proxy;
    }

    private static String buildInterfacesKey(ClassLoader cl, Class<?>[] ics) {
        StringBuilder sb = new StringBuilder();
        for (Class<?> ic : ics) {
            String itf = ic.getName();
            if (!ic.isInterface()) {
                throw new RuntimeException(itf + " is not a interface.");
            }

            Class<?> tmp = null;
            try {
                tmp = Class.forName(itf, false, cl);
            } catch (ClassNotFoundException ignore) {
            }

            if (tmp != ic) {
                throw new IllegalArgumentException(ic + " is not visible from class loader");
            }

            sb.append(itf).append(';');
        }
        return sb.toString();
    }

    private static Class<?> buildProxyClass(ClassLoader cl, Class<?>[] ics, ProtectionDomain domain) {
        ClassGenerator ccp = null;
        try {
            ccp = ClassGenerator.newInstance(cl);

            Set<String> worked = new HashSet<>();
            List<Method> methods = new ArrayList<>();

            String pkg = ics[0].getPackage().getName();
            Class<?> neighbor = ics[0];

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Pass interfaces that are all visible from a single shared ClassLoader; for OSGi, ensure the interface package is exported.
  2. Make sure there is only one copy of the interface class on the classpath (no duplicate jars in different loaders).
  3. If using a custom loader, ensure it delegates to the loader that already defined the interface.
  4. Check ics[0].getClassLoader() can Class.forName each other interface.

Example fix

// before
// interface loaded by bundleA's loader, passed alongside classes from bundleB
Proxy.getProxy(MixedFromBundles.class);

// after
// export the interface package in OSGi and call from a bundle that sees all of them,
// or pass a ClassLoader that can resolve every interface:
Proxy.getProxy(MyService.class); // ensure MyService visible from ics[0]'s loader
Defensive patterns

Strategy: validation

Validate before calling

ClassLoader cl = ics[0].getClassLoader();
for (Class<?> ic : ics) {
    Class<?> resolved = Class.forName(ic.getName(), false, cl);
    if (resolved != ic) {
        throw new IllegalStateException(ic + " not visible from loader " + cl);
    }
}
Proxy.getProxy(ics);

Prevention

When it happens

Trigger: Proxy.getProxy(ics) in an OSGi, Tomcat, or nested-loader environment where ics[0]'s ClassLoader cannot see one of the other interfaces; interface loaded by the boot loader while cl is an app loader that resolves a different copy.

Common situations: OSGi bundles where the interface is not exported; web app with a shared loader vs webapp loader mismatch; the same interface class present in two jars loaded by different loaders (class identity clash); Spring Boot nested jars where the interface is in a parent loader not visible to the child.

Related errors


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/cd8bd0ec2a40a0f7. Report an issue: GitHub.