apache/shenyu · error · IllegalArgumentException
extension clazz (clazz) is not interface!
Error message
extension clazz (clazz) is not interface!
What it means
ShenYu's SPI mechanism (ExtensionLoader.getExtensionLoader) only works with interfaces annotated with @SPI. This IllegalArgumentException is thrown when the Class passed to getExtensionLoader is a concrete class, enum, or other non-interface type. The SPI framework loads implementations via META-INF/shenyu/ resource files keyed by the interface, so only interfaces are valid extension types.
Solutions
- Pass the SPI interface type instead of an implementation class
- Verify the type is an interface: clazz.isInterface() must be true
- Annotate the interface with @org.apache.shenyu.spi.SPI
Example fix
// before ExtensionLoader<LoadBalanceMockImpl> loader = ExtensionLoader.getExtensionLoader(LoadBalanceMockImpl.class); // after ExtensionLoader<LoadBalance> loader = ExtensionLoader.getExtensionLoader(LoadBalance.class);
Defensive patterns
Strategy: validation
Validate before calling
if (!LoadBalance.class.isInterface()) { throw new IllegalArgumentException("must pass an SPI interface"); }
ExtensionLoader<LoadBalance> loader = ExtensionLoader.getExtensionLoader(LoadBalance.class); Type guard
static <T> boolean isSpiInterface(Class<T> c) { return c.isInterface(); } Try / catch
try { loader = ExtensionLoader.getExtensionLoader(clazz); } catch (IllegalArgumentException e) { log.error("Bad SPI type: {}", e.getMessage()); throw e; } Prevention
- Always pass the interface type, never an implementation class
- Mark SPI interfaces with @SPI so intent is visible
- Wrap SPI bootstrap in a startup check/fail-fast init
When it happens
Trigger: Calling ExtensionLoader.getExtensionLoader(SomeConcreteClass.class) where the argument is a class or abstract class rather than an interface.
Common situations: Developer mistakenly passes the implementation class instead of the SPI interface (e.g. getExtensionLoader(LoadBalanceMockImpl.class) instead of getExtensionLoader(LoadBalance.class)); refactoring turned a former interface into a class; copy-paste of the wrong Class literal.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- extension clazz (clazz) without @SPI Annotation
- If the preMatch method was implemented ,the preParse method…
- No implementation defined in /META-INF/services/
- The current provider is of orderly type. Please use…
- The current provider is not of orderly type. Please use…
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/cb2a01cc8bf9d417.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-spi/src/main/java/org/apache/shenyu/spi/ExtensionLoader.java:97
if (!Objects.equals(clazz, ExtensionFactory.class)) {
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getExtensionClassesEntity();
}
}
/**
* Gets extension loader.
*
* @param <T> the type parameter
* @param clazz the clazz
* @param cl the cl
* @return the extension loader.
*/
public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz, final ClassLoader cl) {
Objects.requireNonNull(clazz, "extension clazz is null");
if (!clazz.isInterface()) {
throw new IllegalArgumentException("extension clazz (" + clazz + ") is not interface!");
}
if (!clazz.isAnnotationPresent(SPI.class)) {
throw new IllegalArgumentException("extension clazz (" + clazz + ") without @" + SPI.class + " Annotation");
}
ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) LOADERS.get(clazz);
if (Objects.nonNull(extensionLoader)) {
return extensionLoader;
}
LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz, cl));
return (ExtensionLoader<T>) LOADERS.get(clazz);
}
/**
* Gets extension loader.
*
* @param <T> the type parameter
* @param clazz the clazz
* @return the extension loaderView on GitHub (pinned to 567142e072)