apache/dubbo · error · ClassNotFoundException
Class not found: ${desc}
Error message
Class not found: ${desc} What it means
Inside name2class(ClassLoader cl, String desc), after handling primitive type codes and the 'L' (object) and '[' (array) cases, a default branch throws ClassNotFoundException for any descriptor string that starts with an unrecognized character. This means the descriptor is neither a valid primitive code, an object reference (L...;), nor an array ([...]).
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java:843
return double.class;
case JVM_FLOAT:
return float.class;
case JVM_INT:
return int.class;
case JVM_LONG:
return long.class;
case JVM_SHORT:
return short.class;
case 'L':
// "Ljava/lang/Object;" ==> "java.lang.Object"
desc = desc.substring(1, desc.length() - 1).replace('/', '.');
break;
case '[':
// "[[Ljava/lang/Object;" ==> "[[Ljava.lang.Object;"
desc = desc.replace('/', '.');
break;
default:
throw new ClassNotFoundException("Class not found: " + desc);
}
if (cl == null) {
cl = ClassUtils.getClassLoader();
}
return Class.forName(desc, true, cl);
}
/**
* get class array instance.
*
* @param desc desc.
* @return Class class array.
* @throws ClassNotFoundException
*/
public static Class<?>[] desc2classArray(String desc) throws ClassNotFoundException {
Class<?>[] ret = desc2classArray(ClassUtils.getClassLoader(), desc);
return ret;View on GitHub (pinned to 3a3043227f)
Solutions
- Inspect the desc string in the exception message to see what was passed — it must start with a valid JVM type code.
- If passing a plain class name, use the public name2class(String name) entry, not the internal descriptor parser.
- For object types, ensure descriptors use 'L' prefix and ';' suffix (e.g. 'Ljava/lang/String;').
- For arrays, ensure the '[' prefix is present (e.g. '[Ljava/lang/String;' or '[I').
Example fix
// before — invalid descriptor
ReflectUtils.name2class(cl, "java/lang/String"); // no 'L' prefix
// after — valid JVM descriptor
ReflectUtils.name2class(cl, "Ljava/lang/String;");
// or use plain name entry point
ReflectUtils.forName("java.lang.String"); Defensive patterns
Strategy: validation
Validate before calling
// Validate JVM descriptor format before calling name2class
private static final Set<Character> VALID_PRIMITIVE_CODES =
Set.of('B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 'V');
void validateDescriptor(String desc) {
if (desc == null || desc.isEmpty()) {
throw new IllegalArgumentException("Empty descriptor");
}
char c = desc.charAt(0);
if (c == 'L' || c == '[' || VALID_PRIMITIVE_CODES.contains(c)) return;
throw new IllegalArgumentException(
"Invalid JVM descriptor starting with '" + c + "': " + desc);
} Try / catch
try {
return ReflectUtils.name2class(cl, desc);
} catch (ClassNotFoundException e) {
if (e.getMessage().contains("Class not found:")) {
logger.error("Malformed descriptor: {}", desc);
}
throw e;
} Prevention
- Use the public name2class(String name) entry point for plain class names, not the internal descriptor parser.
- Derive descriptors from Class.getName() or reflection rather than constructing them manually.
- Validate descriptor strings against the JVM spec format in tests.
When it happens
Trigger: Calling ReflectUtils.name2class with a JVM descriptor string that doesn't begin with a recognized type code. For example, passing a descriptor like 'Xjava/lang/String;' or a plain class name like 'java.lang.String' where the method expects internal JVM descriptor format. Note: name2class also handles plain names in its public entry point, but the internal descriptor path is strict.
Common situations: Internal Dubbo serialization code passing a malformed descriptor string, or a custom protocol extension that constructs descriptors incorrectly. The public name2class(String name) entry point handles plain names and delegates to this descriptor-parsing path for array/object forms; a programming error in constructing the descriptor format triggers this.
Related errors
- Method [{}] not found.
- Not found class ${name}, cause: ${e.getMessage()}
- Can not merge result because missing method [ {merger} ] in
- Can not merge result: {e.getMessage()}
- unable to determine bean class from factory's superclass or
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/afbd2fb7d0b3d16f.
Report an issue: GitHub.