{"record":{"id":"6b25718c5203998c","repo":"gradle/gradle","slug":"s-is-a-non-static-inner-class-6b2571","errorCode":null,"errorMessage":"%s is a non-static inner class.","messagePattern":"(.+?) is a non-static inner class\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"platforms/core-configuration/model-core/src/main/java/org/gradle/internal/instantiation/generator/ParamsMatchingConstructorSelector.java","lineNumber":50,"sourceCode":"    @Override\n    public void vetoParameters(ClassGenerator.GeneratedConstructor<?> constructor, Object[] parameters) {\n        // Don't care\n    }\n\n    @Override\n    public <T> ClassGenerator.GeneratedConstructor<? extends T> forType(Class<T> type) throws UnsupportedOperationException {\n        throw new UnsupportedOperationException(\"This constructor selector requires the construction parameters\");\n    }\n\n    @Override\n    public <T> ClassGenerator.GeneratedConstructor<? extends T> forParams(final Class<T> type, Object[] params) {\n        ClassGenerator.GeneratedClass<?> generatedClass = classGenerator.generate(type);\n\n        if (generatedClass.getOuterType() != null && (params.length == 0 || !generatedClass.getOuterType().isInstance(params[0]))) {\n            TreeFormatter formatter = new TreeFormatter();\n            formatter.node(type);\n            formatter.append(\" is a non-static inner class.\");\n            throw new IllegalArgumentException(formatter.toString());\n        }\n\n        List<? extends ClassGenerator.GeneratedConstructor<?>> constructors = generatedClass.getConstructors();\n        if (constructors.size() == 1) {\n            return Cast.uncheckedCast(constructors.get(0));\n        }\n\n        ClassGenerator.GeneratedConstructor<?> match = null;\n        // NOTE: this relies on the constructors being in a predictable order\n        // sorted by the number of parameters the constructor requires\n        for (ClassGenerator.GeneratedConstructor<?> constructor : constructors) {\n            Class<?>[] parameterTypes = constructor.getParameterTypes();\n            // The candidate constructor has fewer parameters than the number of\n            // parameters we were given. This can't be the constructor\n            if (parameterTypes.length < params.length) {\n                continue;\n            }\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/gradle/gradle/blob/534f27719b66953f95cc907aae7f2c1b12f5482d/platforms/core-configuration/model-core/src/main/java/org/gradle/internal/instantiation/generator/ParamsMatchingConstructorSelector.java#L32-L68","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass the enclosing instance first: newInstance(Inner.class, outerRef, otherArgs...).","Or make the nested class static / move it to top level so no outer instance is needed."],"exampleFix":"// before\ninstantiator.newInstance(Inner.class, config);\n// after\ninstantiator.newInstance(Inner.class, outer, config);","handlingStrategy":"validation","validationCode":"Class<?> outer = type.getEnclosingClass();\nif (outer != null && !Modifier.isStatic(type.getModifiers())\n        && (params.length == 0 || !outer.isInstance(params[0]))) {\n    throw new IllegalArgumentException(\"pass the outer instance as the first parameter for \" + type.getName());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["For inner classes under params matching, always pass the enclosing instance as argument zero.","Prefer static nested or top-level classes for types created by reflection-based instantiators."],"tags":["gradle","dependency-injection","inner-class","constructor-selection"],"backgroundTag":"non-static-inner-class-injection","analyzedSha":"534f27719b66953f95cc907aae7f2c1b12f5482d","analyzedAt":"2026-08-22T08:09:12.375Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}