{"record":{"id":"94091984eede1d3f","repo":"quarkusio/quarkus","slug":"no-suitable-projection-constructor-found-for-typ","errorCode":null,"errorMessage":"No suitable projection constructor found for ${type.getName()} (rejected constructor: ${constructor}).","messagePattern":"No suitable projection constructor found for (.+?) \\(rejected constructor: (.+?)\\)\\.","errorType":"exception","errorClass":"PanacheQueryException","httpStatus":null,"severity":"error","filePath":"extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/ProjectionConstructorUtil.java","lineNumber":97,"sourceCode":"            }\n        }\n\n        if (!usableConstructors.isEmpty()) {\n            Constructor<?> selectedConstructor = null;\n            int minParameterCount = Integer.MAX_VALUE;\n            for (Constructor<?> constructor : usableConstructors) {\n                int parameterCount = getProjectionParameters(constructor).size();\n                if (parameterCount < minParameterCount) {\n                    minParameterCount = parameterCount;\n                    selectedConstructor = constructor;\n                }\n            }\n            return selectedConstructor;\n        }\n\n        for (Constructor<?> constructor : constructors) {\n            if (constructor.getParameterCount() > 0) {\n                throw new PanacheQueryException(buildNoSuitableConstructorMessage(type, constructor));\n            }\n        }\n        throw new PanacheQueryException(\"No suitable projection constructor found for \" + type.getName()\n                + \". Projection DTOs require a constructor with at least one parameter.\");\n    }\n\n    public static String getProjectionParameterName(Class<?> parentType, String parentParameter, Parameter parameter,\n            BiFunction<Class<?>, String, String> nestedProjectionBuilder) {\n        String parameterName;\n        if (hasProjectedFieldName(parameter)) {\n            parameterName = getNameFromProjectedFieldName(parameter);\n        } else if (!parameter.isNamePresent()) {\n            throw new PanacheQueryException(\n                    \"Your application must be built with parameter names, this should be the default if\"\n                            + \" using Quarkus project generation. Check the Maven or Gradle compiler configuration to include '-parameters'.\"\n                            + \" When using Kotlin data classes with value classes or default parameters, Panache skips synthetic\"\n                            + \" constructors automatically; if this error persists, annotate the constructor with @ProjectedConstructor\"\n                            + \" or annotate parameters with @ProjectedFieldName.\");","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/panache/panache-hibernate-common/runtime/src/main/java/io/quarkus/panache/hibernate/common/runtime/ProjectionConstructorUtil.java#L79-L115","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\npublic class PersonName {\n    public String name;\n}\n\n// after\npublic class PersonName {\n    public PersonName(String name) { this.name = name; }\n}","handlingStrategy":"validation","validationCode":"// verify at startup that the DTO has a usable projection constructor\nstatic void assertProjectionDto(Class<?> dto) {\n    boolean ok = java.util.Arrays.stream(dto.getConstructors())\n        .anyMatch(c -> c.getParameterCount() > 0);\n    if (!ok) throw new IllegalStateException(dto + \" needs a constructor with parameters for projection\");\n}","typeGuard":null,"tryCatchPattern":"try (PanacheQuery<Entity> q = repo.findAll().project(Dto.class)) {\n    return q.list();\n} catch (PanacheQueryException e) {\n    log.error(\"Projection constructor mismatch for DTO; check select list vs constructor\", e);\n    throw e;\n}","preventionTips":["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"],"tags":["panache","jpa","projection","dto","reflection"],"backgroundTag":"no-suitable-constructor","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}