quarkusio/quarkus · error · PanacheQueryException
No suitable projection constructor found for ${type.getName(
Error message
No suitable projection constructor found for ${type.getName()} (rejected constructor: ${constructor}). What it means
ProjectionConstructorUtil.getProjectionConstructor resolves the constructor used for DTO projections in Panache queries. When no constructor matching the selected parameters exists, it throws a message naming the type and the rejected constructor via buildNoSuitableConstructorMessage. Panache matches projection constructors against the query select items (by parameter count/names), so a DTO whose constructors all mismatch the select list triggers this error.
Source
Thrown at extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/ProjectionConstructorUtil.java:97
}
}
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.");View on GitHub (pinned to e1c734241f)
Solutions
- Add a constructor to the DTO whose parameters match the select items in order and type (e.g. PersonName(String name, int age))
- Compile with -parameters so constructor parameter names are available for matching
- Annotate constructor parameters with @ProjectedFieldName("fieldName") to map them explicitly when names differ
- Annotate the intended constructor with @ProjectedConstructor if multiple constructors exist
- Verify the query select list (select foo.bar, foo.baz) matches the DTO constructor parameter count/types
Example fix
// before
public class PersonName {
public String name;
}
// after
public class PersonName {
public PersonName(String name) { this.name = name; }
} Defensive patterns
Strategy: validation
Validate before calling
// verify at startup that the DTO has a usable projection constructor
static void assertProjectionDto(Class<?> dto) {
boolean ok = java.util.Arrays.stream(dto.getConstructors())
.anyMatch(c -> c.getParameterCount() > 0);
if (!ok) throw new IllegalStateException(dto + " needs a constructor with parameters for projection");
} Try / catch
try (PanacheQuery<Entity> q = repo.findAll().project(Dto.class)) {
return q.list();
} catch (PanacheQueryException e) {
log.error("Projection constructor mismatch for DTO; check select list vs constructor", e);
throw e;
} Prevention
- Keep the DTO constructor's parameter order/types aligned with the select items
- Compile with -parameters so name-based matching works
- Use records for projection DTOs (canonical constructor fits naturally)
- When multiple constructors exist, mark the intended one with @ProjectedConstructor
When it happens
Trigger: Using query.project(Class) with a DTO that has no constructor whose parameter list matches the select items; the single-parameter constructor examined was rejected (wrong type or name mismatch) so Panache reports it as rejected; multiple candidate constructors where none align with the projection columns.
Common situations: DTO with only a no-arg constructor plus setters (Panache projections need constructor-based mapping); DTO constructor parameter names not compiled in (missing -parameters) so matching by name fails; changed select list without updating the DTO constructor; Lombok @AllArgsConstructor with fields in a different order than the select items.
Related errors
- No suitable projection constructor found for ${type.getName(
- Your application must be built with parameter names, this sh
- Unable to read ProjectedFieldName value
- Missing ProjectedFieldName annotation
- The annotation ProjectedFieldName must have a non-empty valu
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/94091984eede1d3f.
Report an issue: GitHub.