apache/shenyu · error · IllegalStateException

load extension resources error

Error message

load extension resources error

What it means

loadResources reads the interface's META-INF/shenyu/ resource URLs, parses name->classPath entries, and loads each class. This IllegalStateException wraps a ClassNotFoundException: an entry in the SPI resource file names a class that the classloader cannot find.

Solutions

  1. Add the jar/module containing the listed class to the classpath
  2. Correct the classPath entry in META-INF/shenyu/<interface-fqcn>
  3. Remove stale entries for classes that no longer exist after an upgrade
  4. Verify packaging (e.g. shenyu-dist/Makefile) includes the extension module

Example fix

// before (META-INF/shenyu/org.apache.shenyu.loadbalance.LoadBalance)
roundRobin=org.apache.shenyu.loadbalance.RoundRobinLoadBalance
// after — class moved packages, update the entry
roundRobin=org.apache.shenyu.spi.loadbalance.RoundRobinLoadBalance
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check that the class named in the resource is loadable
Class.forName("org.apache.shenyu.loadbalance.RoundRobinLoadBalance", true, classLoader);

Try / catch

try { T ext = loader.getJoin(name); } catch (IllegalStateException e) { Throwable cause = e.getCause(); log.error("SPI resource load failed, cause: {}", cause, cause); throw e; }

Prevention

When it happens

Trigger: A META-INF/shenyu/<interface> file contains a classPath (fully-qualified name) whose class is not on the classpath when any extension of that interface is first loaded.

Common situations: Optional implementation jar not included in the deployment; class moved/renamed in a version upgrade while the old resource file entry remains; fat-jar packaging omitted a module; typos in the FQCN.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/1a5399fdf0726461. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-spi/src/main/java/org/apache/shenyu/spi/ExtensionLoader.java:295

                }
            }
        } catch (IOException t) {
            LOG.error("load extension class error {}", fileName, t);
        }
    }
    
    private void loadResources(final Map<String, ClassEntity> classes, final URL url) throws IOException {
        try (InputStream inputStream = url.openStream()) {
            Properties properties = new Properties();
            properties.load(inputStream);
            properties.forEach((k, v) -> {
                String name = (String) k;
                String classPath = (String) v;
                if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) {
                    try {
                        loadClass(classes, name, classPath);
                    } catch (ClassNotFoundException e) {
                        throw new IllegalStateException("load extension resources error", e);
                    }
                }
            });
        } catch (IOException e) {
            throw new IllegalStateException("load extension resources error", e);
        }
    }
    
    private void loadClass(final Map<String, ClassEntity> classes,
                           final String name, final String classPath) throws ClassNotFoundException {
        Class<?> subClass = Objects.nonNull(this.classLoader) ? Class.forName(classPath, true, this.classLoader) : Class.forName(classPath);
        if (!clazz.isAssignableFrom(subClass)) {
            throw new IllegalStateException("load extension resources error," + subClass + " subtype is not of " + clazz);
        }
        if (!subClass.isAnnotationPresent(Join.class)) {
            throw new IllegalStateException("load extension resources error," + subClass + " without @" + Join.class + " annotation");
        }
        ClassEntity oldClassEntity = classes.get(name);

View on GitHub (pinned to 567142e072)