gradle/gradle · error · IllegalArgumentException
%s is a non-static inner class.
Error message
%s is a non-static inner class.
What it means
Jsr330ConstructorSelector.validateType rejects non-static inner classes before any constructor selection runs: the generated decorator subclass cannot synthesize the implicit reference to an enclosing instance that a non-static inner class requires, so instantiation would be impossible. Interfaces, top-level classes, and static nested classes pass.
Source
Thrown at platforms/core-configuration/model-core/src/main/java/org/gradle/internal/instantiation/generator/Jsr330ConstructorSelector.java:71
CachedConstructor constructor = constructorCache.get(type, () -> {
try {
validateType(type);
ClassGenerator.GeneratedClass<?> implClass = classGenerator.generate(type);
ClassGenerator.GeneratedConstructor<?> generatedConstructor = InjectUtil.selectConstructor(implClass, type);
return CachedConstructor.of(generatedConstructor);
} catch (RuntimeException e) {
return CachedConstructor.of(e);
}
});
return Cast.uncheckedCast(constructor.getConstructor());
}
private static <T> void validateType(Class<T> type) {
if (!type.isInterface() && type.getEnclosingClass() != null && !Modifier.isStatic(type.getModifiers())) {
TreeFormatter formatter = new TreeFormatter();
formatter.node(type);
formatter.append(" is a non-static inner class.");
throw new IllegalArgumentException(formatter.toString());
}
}
public static class CachedConstructor {
private final ClassGenerator.GeneratedConstructor<?> constructor;
private final RuntimeException error;
private CachedConstructor(ClassGenerator.GeneratedConstructor<?> constructor, RuntimeException error) {
this.constructor = constructor;
this.error = error;
}
public ClassGenerator.GeneratedConstructor<?> getConstructor() {
if (error != null) {
throw error;
}
return constructor;
}View on GitHub (pinned to 534f27719b)
Solutions
- Declare the nested class static: public static class Helper.
- Move the class to top level.
- Model the type as an interface, which passes this check automatically.
Example fix
// before
class Outer {
class Helper {} // carries implicit Outer.this
}
// after
class Outer {
static class Helper {}
} Defensive patterns
Strategy: type-guard
Type guard
static boolean isInjectable(Class<?> type) {
return type.isInterface()
|| type.getEnclosingClass() == null
|| Modifier.isStatic(type.getModifiers());
} Prevention
- Default nested helper classes to static.
- Add a build-time scan over the types handed to the instantiator to catch non-static nested classes early.
When it happens
Trigger: Passing Class<Helper> where Helper is a nested class without the static modifier to a JSR-330-based instantiator; validation runs once per type inside the constructor cache and the failure is replayed for every later attempt.
Common situations: Helper classes nested inside plugin classes for packaging convenience; top-level classes moved into nested positions without adding static; note that Kotlin nested classes are fine unless marked inner.
Related errors
- %s is a non-static inner class.
- Unable to select constructor for non-static inner class %s.
- Too many parameters provided for constructor for type %s. Ex
- Unable to determine constructor argument #%s: value %s not a
- Unable to determine constructor argument #%s: null value is
AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22).
Data as JSON: /api/errors/f887b7cc4c93c60d.
Report an issue: GitHub.