apache/shenyu · error · IllegalStateException
load extension resources error,Duplicate class clazz name…
Error message
load extension resources error,Duplicate class clazz name name on oldClass or subClass
What it means
ShenYu's SPI ExtensionLoader loads extension implementations from META-INF/shenyu/ resource files, mapping a name to a class. When two different classes claim the same extension name within one extension interface's resources, it cannot decide which one is correct and throws this IllegalStateException during loadResources → loadClass.
Solutions
- Rename the extension name in your META-INF/shenyu/<interface-fqcn> file so it is unique
- Remove the duplicate/conflicting registration line or the redundant jar containing the conflicting implementation
- If the classes are actually identical, delete one of the duplicate declarations
Example fix
// before (META-INF/shenyu/org.apache.shenyu.spi.HttpParamConverter) timeConverter=org.apache.shenyu.example.TimeConverter // after (unique name) myTimeConverter=org.apache.shenyu.example.TimeConverter
Defensive patterns
Strategy: validation
Validate before calling
Map<String,String> names = new HashMap<>();
for (String line : Files.readAllLines(spiFile)) {
String name = line.split("=", 2)[0].trim();
if (names.containsKey(name)) throw new IllegalStateException("duplicate SPI name: " + name);
names.put(name, line);
} Try / catch
try {
extension = ExtensionLoader.getExtensionLoader(MySpi.class).getJoin("myName");
} catch (IllegalStateException e) {
if (e.getMessage().contains("Duplicate class")) {
log.error("Conflicting SPI registration for name", e);
} else throw e;
} Prevention
- Prefix custom extension names with your organization/product name
- Audit META-INF/shenyu/ files in all jars on the classpath before release
- Never reuse an upstream ShenYu extension name for a different class
When it happens
Trigger: Two SPI registration files (or duplicate entries in one file) under META-INF/shenyu/ declare the same extension `name` mapped to different implementation classes of the same @SPI interface.
Common situations: Merging upstream code with a locally added extension that reused an existing name; adding a custom implementation in a jar that collides with a ShenYu built-in extension name; copy-pasting an SPI config line without changing the name key.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- No implementation defined in /META-INF/services/
- name is error
- load extension resources error
- shenyu.jwt.secretKey is not configured. In a multi-instance…
- shenyu discovery mode current didn't support
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/ba912bf8f2e7b7c7.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-spi/src/main/java/org/apache/shenyu/spi/ExtensionLoader.java:319
}
}
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.
*
* @param <T> the type parameter.
*/
private static final class Holder<T> {
private volatile T value;
private Integer order;
/**
* Gets value.
*View on GitHub (pinned to 567142e072)