gradle/gradle · error · IllegalArgumentException

%s has no constructor that is annotated with @Inject (javax.

Error message

%s has no constructor that is annotated with @Inject (javax.inject.Inject) for dependency injection.

What it means

When a type declares multiple constructors, InjectUtil requires exactly one to be annotated with @Inject. If none carries the annotation, constructor selection is impossible and Gradle throws IllegalArgumentException naming the type before instantiation proceeds.

Source

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

                formatter.append(" should be ");
                appendAnnotatedWithInject(formatter);
                throw new IllegalArgumentException(formatter.toString());
            }
        }

        List<ClassGenerator.GeneratedConstructor<T>> injectConstructors = new ArrayList<ClassGenerator.GeneratedConstructor<T>>();
        for (ClassGenerator.GeneratedConstructor<T> constructor : constructors) {
            if (constructor.getAnnotation(Inject.class) != null) {
                injectConstructors.add(constructor);
            }
        }

        if (injectConstructors.isEmpty()) {
            TreeFormatter formatter = new TreeFormatter();
            formatter.node(reportAs);
            formatter.append(" has no constructor that is ");
            appendAnnotatedWithInject(formatter);
            throw new IllegalArgumentException(formatter.toString());
        }
        if (injectConstructors.size() > 1) {
            TreeFormatter formatter = new TreeFormatter();
            formatter.node(reportAs);
            formatter.append(" has multiple constructors that are ");
            appendAnnotatedWithInject(formatter);
            throw new IllegalArgumentException(formatter.toString());
        }

        return injectConstructors.get(0);
    }

    private static void appendAnnotatedWithInject(TreeFormatter formatter) {
        formatter.append("annotated with @Inject (");
        formatter.append(Inject.class.getName());
        formatter.append(") for dependency injection.");
    }

View on GitHub (pinned to 534f27719b)

Solutions

  1. Designate one canonical constructor and annotate it with @Inject.
  2. Delete redundant constructors if they all populate the same fields.
  3. In Groovy, target the constructor explicitly: @Inject MyType(String name) { }.

Example fix

// before
MyType() {}
MyType(String name) { this.name = name; }
// after
MyType() {}
@Inject
MyType(String name) { this.name = name; }
Defensive patterns

Strategy: validation

Validate before calling

long injectCount = Arrays.stream(type.getDeclaredConstructors())
    .filter(c -> c.isAnnotationPresent(javax.inject.Inject.class)).count();
if (type.getDeclaredConstructors().length > 1 && injectCount != 1) {
    throw new IllegalStateException("type needs exactly one @Inject constructor: " + type.getName());
}

Prevention

When it happens

Trigger: A type with overloaded constructors, for example MyType(), MyType(String), MyType(String, int), with no @Inject on any of them, instantiated through the JSR-330 selector.

Common situations: Backwards-compatible constructors accumulated across releases; Lombok-generated constructors plus a hand-written one; Groovy classes where @Inject was placed on the class instead of a constructor.

Related errors


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