apache/dubbo · error · RuntimeException

Illegal constructor: ${cls.getName()}

Error message

Illegal constructor: ${cls.getName()}

What it means

When PojoUtils needs to instantiate a class during realization, it first tries the no-arg constructor. If that fails, it checks getDeclaredConstructors(). Per the Java spec, that method returns an empty array for interfaces, primitive types, array classes, and void. An empty array means there is no way to instantiate, so a RuntimeException is thrown naming the class.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java:694

        } catch (Exception t) {
            return newInstance(cls);
        }
    }

    private static Object newInstance(Class<?> cls) {
        try {
            return cls.getDeclaredConstructor().newInstance();
        } catch (Exception t) {
            Constructor<?>[] constructors = cls.getDeclaredConstructors();
            /*
             From Javadoc java.lang.Class#getDeclaredConstructors
             This method returns an array of Constructor objects reflecting all the constructors
             declared by the class represented by this Class object.
             This method returns an array of length 0,
             if this Class object represents an interface, a primitive type, an array class, or void.
            */
            if (constructors.length == 0) {
                throw new RuntimeException("Illegal constructor: " + cls.getName());
            }
            Throwable lastError = null;
            Arrays.sort(constructors, Comparator.comparingInt(a -> a.getParameterTypes().length));
            for (Constructor<?> constructor : constructors) {
                try {
                    constructor.setAccessible(true);
                    Object[] parameters = Arrays.stream(constructor.getParameterTypes())
                            .map(PojoUtils::getDefaultValue)
                            .toArray();
                    return constructor.newInstance(parameters);
                } catch (Exception e) {
                    lastError = e;
                }
            }
            throw new RuntimeException(lastError.getMessage(), lastError);
        }
    }

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Ensure the target type for realization is a concrete instantiable class with at least one constructor, not an interface or primitive.
  2. If the type is an interface, provide the concrete implementation class name in the generic map ('class' key) or configure the provider to return a concrete type.
  3. Check that the method return type or parameter type metadata on the provider side points to a concrete class.
  4. Verify the GENERIC_WITH_CLZ setting and that the serialized form includes the concrete class name.

Example fix

// before — interface as target type
PojoUtils.realize(map, MyInterface.class);
// after — concrete class
PojoUtils.realize(map, MyInterfaceImpl.class);
// or include class name in the map
map.put("class", "com.example.MyInterfaceImpl");
PojoUtils.realize(map, MyInterface.class);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the class is instantiable before passing to realize
if (type.isInterface() || type.isPrimitive() || type.isArray() || type == Void.class) {
    throw new IllegalArgumentException(
        "Cannot realize into non-instantiable type: " + type.getName()
        + ". Provide a concrete class.");
}
PojoUtils.realize(map, type);

Type guard

// Type guard for instantiable types
static boolean isInstantiable(Class<?> cls) {
    return !cls.isInterface() && !cls.isPrimitive()
        && !cls.isArray() && cls != Void.class;
}

Prevention

When it happens

Trigger: Calling PojoUtils.newInstance(cls) (indirectly via realize) where cls is an interface, a primitive type (int.class), an array class, or void.class. These types have no constructors and cannot be instantiated via reflection.

Common situations: A generic RPC where the target type metadata resolves to an interface instead of a concrete class, or a deserialization target is incorrectly specified as a primitive or array type. The realize0 logic should normally handle interfaces by finding an implementation, but if the code path reaches newInstance with an interface, it fails here.

Related errors


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