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
- Pass interfaces that are all visible from a single shared ClassLoader; for OSGi, ensure the interface package is exported.
- Make sure there is only one copy of the interface class on the classpath (no duplicate jars in different loaders).
- If using a custom loader, ensure it delegates to the loader that already defined the interface.
- 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
- Ensure all interfaces are loaded by the same ClassLoader (or a loader visible to ics[0]'s loader).
- In OSGi, export the interface packages.
- Remove duplicate copies of interface jars from different loaders.
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
- ${cause.message}
- Method [{}] unimplemented.
- interface limit exceeded
- {} is not a interface.
- non-public interfaces from different packages
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/cd8bd0ec2a40a0f7.
Report an issue: GitHub.