gradle/gradle · error · IllegalArgumentException

%s is a non-static inner class.

Error message

%s is a non-static inner class.

What it means

ParamsMatchingConstructorSelector.forParams can handle inner classes, but only when the enclosing instance is supplied: if the generated class has an outer type and either zero params were given or params[0] is not an instance of the enclosing class, it throws '%s is a non-static inner class.' before matching constructors.

Source

Thrown at platforms/core-configuration/model-core/src/main/java/org/gradle/internal/instantiation/generator/ParamsMatchingConstructorSelector.java:50

    @Override
    public void vetoParameters(ClassGenerator.GeneratedConstructor<?> constructor, Object[] parameters) {
        // Don't care
    }

    @Override
    public <T> ClassGenerator.GeneratedConstructor<? extends T> forType(Class<T> type) throws UnsupportedOperationException {
        throw new UnsupportedOperationException("This constructor selector requires the construction parameters");
    }

    @Override
    public <T> ClassGenerator.GeneratedConstructor<? extends T> forParams(final Class<T> type, Object[] params) {
        ClassGenerator.GeneratedClass<?> generatedClass = classGenerator.generate(type);

        if (generatedClass.getOuterType() != null && (params.length == 0 || !generatedClass.getOuterType().isInstance(params[0]))) {
            TreeFormatter formatter = new TreeFormatter();
            formatter.node(type);
            formatter.append(" is a non-static inner class.");
            throw new IllegalArgumentException(formatter.toString());
        }

        List<? extends ClassGenerator.GeneratedConstructor<?>> constructors = generatedClass.getConstructors();
        if (constructors.size() == 1) {
            return Cast.uncheckedCast(constructors.get(0));
        }

        ClassGenerator.GeneratedConstructor<?> match = null;
        // NOTE: this relies on the constructors being in a predictable order
        // sorted by the number of parameters the constructor requires
        for (ClassGenerator.GeneratedConstructor<?> constructor : constructors) {
            Class<?>[] parameterTypes = constructor.getParameterTypes();
            // The candidate constructor has fewer parameters than the number of
            // parameters we were given. This can't be the constructor
            if (parameterTypes.length < params.length) {
                continue;
            }

View on GitHub (pinned to 534f27719b)

Solutions

  1. Pass the enclosing instance first: newInstance(Inner.class, outerRef, otherArgs...).
  2. Or make the nested class static / move it to top level so no outer instance is needed.

Example fix

// before
instantiator.newInstance(Inner.class, config);
// after
instantiator.newInstance(Inner.class, outer, config);
Defensive patterns

Strategy: validation

Validate before calling

Class<?> outer = type.getEnclosingClass();
if (outer != null && !Modifier.isStatic(type.getModifiers())
        && (params.length == 0 || !outer.isInstance(params[0]))) {
    throw new IllegalArgumentException("pass the outer instance as the first parameter for " + type.getName());
}

Prevention

When it happens

Trigger: forParams(Inner.class, ...) with an empty params array, or with the outer instance passed in the wrong position so params[0] is not an instance of the enclosing class.

Common situations: Switching a type to a nested class without updating instantiation call sites; forgetting that under this selector an inner-class construction requires the outer instance as the very first argument.

Related errors


AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22). Data as JSON: /api/errors/6b25718c5203998c. Report an issue: GitHub.