{"record":{"id":"4283132df4905a88","repo":"quarkusio/quarkus","slug":"no-suitable-projection-constructor-found-for-typ-428313","errorCode":null,"errorMessage":"No suitable projection constructor found for ${type.getName()}. Projection DTOs require a constructor with at least one parameter.","messagePattern":"No suitable projection constructor found for (.+?)\\. Projection DTOs require a constructor with at least one parameter\\.","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":100,"sourceCode":"        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.\");\n        } else {\n            try {\n                Field field = parentType.getDeclaredField(parameter.getName());","sourceCodeStart":82,"sourceCodeEnd":118,"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#L82-L118","documentation":"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.","triggerScenarios":"Calling PanacheQuery.project(SomeDto.class) where SomeDto only declares a no-arg constructor (default constructor, or a class with no explicit constructors at all).","commonSituations":"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.","solutions":["Add a constructor with at least one parameter matching the select items, e.g. public MyDto(String name)","Alternatively convert the DTO to a Java record whose canonical constructor serves as the projection constructor","If using Lombok, ensure @AllArgsConstructor (or an explicit constructor) is generated and -parameters is enabled","Map fields manually instead by selecting into the entity and converting after the query"],"exampleFix":"// before\npublic class PersonDto {\n    private String name;\n    public String getName() { return name; }\n}\n\n// after\npublic record PersonDto(String name) {}","handlingStrategy":"validation","validationCode":"static void assertHasParameterizedConstructor(Class<?> dto) {\n    if (java.util.Arrays.stream(dto.getConstructors()).allMatch(c -> c.getParameterCount() == 0)) {\n        throw new IllegalStateException(dto.getName() + \" cannot be used as a projection: no constructor with parameters\");\n    }\n}","typeGuard":"boolean isUsableProjection(Class<?> dto) {\n    return java.util.Arrays.stream(dto.getConstructors())\n        .anyMatch(c -> c.getParameterCount() > 0);\n}","tryCatchPattern":"try {\n    return repo.findAll().project(Dto.class).list();\n} catch (PanacheQueryException e) {\n    throw new IllegalStateException(\"DTO \" + Dto.class.getSimpleName() + \" lacks a projection constructor\", e);\n}","preventionTips":["Prefer records or explicit all-args constructors for projection DTOs","Don't reuse JavaBean-style setter-only DTOs for Panache projections","With Lombok, include @AllArgsConstructor and compile with -parameters","Review DTOs used with .project(...) during refactors that remove constructors"],"tags":["panache","jpa","projection","dto","constructor"],"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-15T02:17:10.978Z"}