apache/shenyu · error · IllegalStateException

load extension resources error,subClass subtype is not of…

Error message

load extension resources error,subClass subtype is not of clazz

What it means

loadClass verifies every class found in an SPI resource file is actually assignable to the SPI interface (clazz.isAssignableFrom). If a registered classPath names a class that does not implement/extend the interface, this IllegalStateException is thrown: '<subClass> subtype is not of <clazz>'.

Solutions

  1. Ensure the mapped class implements the SPI interface (or extends it)
  2. Fix the classPath entry in META-INF/shenyu/<interface-fqcn> to point at the correct implementation
  3. Rename/remove resource files that were copied from another interface
  4. After refactoring, update both the class package and its SPI registration file

Example fix

// before (META-INF/shenyu/org.apache.shenyu.spi.LoadBalance)
random=org.apache.shenyu.spi.hash.HashImpl // does not implement LoadBalance
// after
random=org.apache.shenyu.spi.balance.RandomLoadBalance
Defensive patterns

Strategy: validation

Validate before calling

if (!LoadBalance.class.isAssignableFrom(implClass)) { throw new IllegalStateException(implClass + " must implement LoadBalance before registration"); }

Try / catch

try { T ext = loader.getJoin(name); } catch (IllegalStateException e) { log.error("Bad SPI registration: {}", e.getMessage()); throw e; }

Prevention

When it happens

Trigger: A META-INF/shenyu/<interface-fqcn> file maps a name to a class that does not implement that interface.

Common situations: Copy-pasted resource file entries pointing at the wrong class; class no longer implements the interface after a refactor; wrong resource file name so entries belong to another interface; duplicate resource files merged from multiple jars.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

                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);
        if (Objects.isNull(oldClassEntity)) {
            Join joinAnnotation = subClass.getAnnotation(Join.class);
            ClassEntity classEntity = new ClassEntity(name, joinAnnotation.order(), subClass, joinAnnotation.isSingleton());
            classes.put(name, classEntity);
        } else if (!Objects.equals(oldClassEntity.getClazz(), subClass)) {
            throw new IllegalStateException("load extension resources error,Duplicate class " + clazz.getName() + " name "
                    + name + " on " + oldClassEntity.getClazz().getName() + " or " + subClass.getName());
        }
    }
    
    /**
     * The type Holder.
     *

View on GitHub (pinned to 567142e072)