{"record":{"id":"148ba77ec691fdf9","repo":"quarkusio/quarkus","slug":"unable-to-perform-a-projection-on-a-select-disti-148ba7","errorCode":null,"errorMessage":"Unable to perform a projection on a 'select [distinct]? new' query: ${query}","messagePattern":"Unable to perform a projection on a 'select \\[distinct\\]\\? new' query: (.+?)","errorType":"exception","errorClass":"PanacheQueryException","httpStatus":null,"severity":"error","filePath":"extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonAbstractPanacheQueryImpl.java","lineNumber":111,"sourceCode":"    protected abstract void disableFilter(SessionType session, String filter);\n\n    // Builder\n\n    public CommonAbstractPanacheQueryImpl<Entity, SessionType> sort(Sort sort) {\n        this.sort = sort;\n        return this;\n    }\n\n    public <T> CommonAbstractPanacheQueryImpl<T, SessionType> project(Class<T> type) {\n        String selectQuery = query;\n        if (PanacheJpaUtil.isNamedQuery(query)) {\n            selectQuery = NamedQueryUtil.getNamedQuery(query.substring(1));\n        }\n\n        String lowerCasedTrimmedQuery = PanacheJpaUtil.trimForAnalysis(selectQuery);\n        if (lowerCasedTrimmedQuery.startsWith(\"select new \")\n                || lowerCasedTrimmedQuery.startsWith(\"select distinct new \")) {\n            throw new PanacheQueryException(\"Unable to perform a projection on a 'select [distinct]? new' query: \" + query);\n        }\n\n        // If the query starts with a select clause, we pass it on to ORM which can handle that via a projection type\n        if (lowerCasedTrimmedQuery.startsWith(\"select \")) {\n            // I think projections do not change the result count, so we can keep the custom count query\n            return newQuery(query, customCountQueryForSpring, type);\n        }\n\n        // FIXME: this assumes the query starts with \"FROM \" probably?\n\n        // build select clause with a constructor expression\n        String selectClause = \"SELECT \" + getParametersFromClass(type, null);\n        // I think projections do not change the result count, so we can keep the custom count query\n        return newQuery(selectClause + selectQuery, customCountQueryForSpring, null);\n    }\n\n    private StringBuilder getParametersFromClass(Class<?> type, String parentParameter) {\n        BiFunction<Class<?>, String, String> nestedProjectionBuilder = (nestedType, parameterName) -> getParametersFromClass(","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonAbstractPanacheQueryImpl.java#L93-L129","documentation":"project(Class) builds a projection query; it refuses queries using 'select new ...' constructor expressions because Panache's projection mechanism cannot rewrite them, throwing PanacheQueryException with the offending query text.","triggerScenarios":"Calling .project(X.class) on a PanacheQuery created from an HQL string starting with 'select new ' or 'select distinct new ', including a '#'-named query resolving to such a query.","commonSituations":"Combining a constructor-expression DTO query with project(); copying a native 'select new' snippet into a panache query then adding projection; generic query builders that always call project().","solutions":["Remove the 'select new' clause and use a plain 'from Entity' query with .project(Dto.class)","If you need constructor expressions, drop .project() and use the 'select new' query directly with the DTO as result type","For named queries, keep the ORDER/simple select and project from the entity query"],"exampleFix":"// before\nfind(\"select new com.acme.Dto(p.name) from Person p\").project(Dto.class)\n// after\nfind(\"from Person p\").project(Dto.class)\n// or without project():\nfind(\"select new com.acme.Dto(p.name) from Person p\").list()","handlingStrategy":"validation","validationCode":"String q = namedQuery ? NamedQueryUtil.getNamedQuery(panacheQuery.substring(1)) : panacheQuery;\nString lower = q.toLowerCase().trim();\nif (lower.startsWith(\"select new \") || lower.startsWith(\"select distinct new \"))\n    throw new IllegalArgumentException(\"Cannot combine .project() with a 'select new' query\");","typeGuard":null,"tryCatchPattern":"try {\n    return pq.project(Dto.class);\n} catch (PanacheQueryException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"select [distinct]? new\")) {\n        throw new IllegalArgumentException(\"Use the 'select new' query directly instead of .project()\", e);\n    }\n    throw e;\n}","preventionTips":["Use .project(Dto.class) only on 'from Entity' or plain 'select' queries","Rely on project() instead of writing 'select new' constructor expressions, or vice versa — pick one","Check named query text before adding .project() calls"],"tags":["quarkus","panache","hibernate-reactive","projection","hql"],"backgroundTag":"projection-on-select-new-query","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}