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

  1. Inspect the desc string in the exception message to see what was passed — it must start with a valid JVM type code.
  2. If passing a plain class name, use the public name2class(String name) entry, not the internal descriptor parser.
  3. For object types, ensure descriptors use 'L' prefix and ';' suffix (e.g. 'Ljava/lang/String;').
  4. 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

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


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/afbd2fb7d0b3d16f. Report an issue: GitHub.