quarkusio/quarkus · error · PanacheQueryException

No suitable projection constructor found for ${type.getName(

Error message

No suitable projection constructor found for ${type.getName()}. Projection DTOs require a constructor with at least one parameter.

What it means

ProjectionConstructorUtil.getProjectionConstructor throws this when every constructor of the projection DTO has zero parameters, i.e. the type has no constructor with at least one argument. Panache DTO projections are built by calling a constructor with the selected columns as arguments, so a parameterless-only DTO cannot be used for projection.

Source

Thrown at extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/ProjectionConstructorUtil.java:100

        if (!usableConstructors.isEmpty()) {
            Constructor<?> selectedConstructor = null;
            int minParameterCount = Integer.MAX_VALUE;
            for (Constructor<?> constructor : usableConstructors) {
                int parameterCount = getProjectionParameters(constructor).size();
                if (parameterCount < minParameterCount) {
                    minParameterCount = parameterCount;
                    selectedConstructor = constructor;
                }
            }
            return selectedConstructor;
        }

        for (Constructor<?> constructor : constructors) {
            if (constructor.getParameterCount() > 0) {
                throw new PanacheQueryException(buildNoSuitableConstructorMessage(type, constructor));
            }
        }
        throw new PanacheQueryException("No suitable projection constructor found for " + type.getName()
                + ". Projection DTOs require a constructor with at least one parameter.");
    }

    public static String getProjectionParameterName(Class<?> parentType, String parentParameter, Parameter parameter,
            BiFunction<Class<?>, String, String> nestedProjectionBuilder) {
        String parameterName;
        if (hasProjectedFieldName(parameter)) {
            parameterName = getNameFromProjectedFieldName(parameter);
        } else if (!parameter.isNamePresent()) {
            throw new PanacheQueryException(
                    "Your application must be built with parameter names, this should be the default if"
                            + " using Quarkus project generation. Check the Maven or Gradle compiler configuration to include '-parameters'."
                            + " When using Kotlin data classes with value classes or default parameters, Panache skips synthetic"
                            + " constructors automatically; if this error persists, annotate the constructor with @ProjectedConstructor"
                            + " or annotate parameters with @ProjectedFieldName.");
        } else {
            try {
                Field field = parentType.getDeclaredField(parameter.getName());

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add a constructor with at least one parameter matching the select items, e.g. public MyDto(String name)
  2. Alternatively convert the DTO to a Java record whose canonical constructor serves as the projection constructor
  3. If using Lombok, ensure @AllArgsConstructor (or an explicit constructor) is generated and -parameters is enabled
  4. Map fields manually instead by selecting into the entity and converting after the query

Example fix

// before
public class PersonDto {
    private String name;
    public String getName() { return name; }
}

// after
public record PersonDto(String name) {}
Defensive patterns

Strategy: validation

Validate before calling

static void assertHasParameterizedConstructor(Class<?> dto) {
    if (java.util.Arrays.stream(dto.getConstructors()).allMatch(c -> c.getParameterCount() == 0)) {
        throw new IllegalStateException(dto.getName() + " cannot be used as a projection: no constructor with parameters");
    }
}

Type guard

boolean isUsableProjection(Class<?> dto) {
    return java.util.Arrays.stream(dto.getConstructors())
        .anyMatch(c -> c.getParameterCount() > 0);
}

Try / catch

try {
    return repo.findAll().project(Dto.class).list();
} catch (PanacheQueryException e) {
    throw new IllegalStateException("DTO " + Dto.class.getSimpleName() + " lacks a projection constructor", e);
}

Prevention

When it happens

Trigger: Calling PanacheQuery.project(SomeDto.class) where SomeDto only declares a no-arg constructor (default constructor, or a class with no explicit constructors at all).

Common situations: JavaBean-style DTO designed for setter binding reused as a Panache projection; records are fine but plain classes with only default constructors are not; forgetting that projection requires constructor mapping unlike Hibernate bean mapping.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/4283132df4905a88. Report an issue: GitHub.