{"record":{"id":"f35b278c48ff3df7","repo":"quarkusio/quarkus","slug":"unable-to-perform-a-projection-on-a-select-disti","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-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java","lineNumber":138,"sourceCode":"\n    // Builder\n\n    public CommonPanacheQueryImpl<Entity> sort(Sort sort) {\n        this.sort = sort;\n        return this;\n    }\n\n    public <T> CommonPanacheQueryImpl<T> project(Class<T> type) {\n        String selectQuery = query;\n        if (PanacheJpaUtil.isNamedQuery(query)) {\n            SelectionQuery<?> q = session.createNamedSelectionQuery(query.substring(1));\n            selectQuery = getQueryString(q);\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 new CommonPanacheQueryImpl<>(this, 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        AtomicReference<String> cachedProjection = ProjectionQueryCache.get(type);\n        if (cachedProjection.get() == null) {\n            cachedProjection.set(\"SELECT \" + getParametersFromClass(type, null));\n        }\n        String selectClause = cachedProjection.get();\n        // I think projections do not change the result count, so we can keep the custom count query\n        return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, customCountQueryForSpring, null);","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java#L120-L156","documentation":"Panache's project(Class) maps result rows onto a projection type. Queries using constructor expressions — 'select new com.Foo(...)' or 'select distinct new ...' — already instantiate objects themselves, so applying another projection is ambiguous and Panache rejects it with PanacheQueryException.","triggerScenarios":"Calling .project(SomeDto.class) on a query string that begins with 'select new ' or 'select distinct new '.","commonSituations":"Refactoring a DTO-constructor query to the project() API; Spring-Data-style projections copied over that already contain 'select new'; generated queries from builders that always emit a select clause.","solutions":["Remove .project(...) and let the 'select new' constructor expression do the mapping","Strip the 'select new ...' clause and select plain entity fields, then use project(Dto.class)","Use a literal 'select field1, field2' query with project() instead"],"exampleFix":"// before\nPanacheQuery<Person> q = Person.find(\"select new com.acme.PersonView(p.name) from Person p\").project(PersonView.class);\n// after\nPanacheQuery<Person> q = Person.find(\"select p.name from Person p\").project(PersonView.class);","handlingStrategy":"validation","validationCode":"String lc = query.toLowerCase().trim();\nif (lc.startsWith(\"select new \") || lc.startsWith(\"select distinct new \")) {\n    throw new IllegalArgumentException(\"Remove .project(); query already uses a constructor expression\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return find(query, params).project(dtoClass);\n} catch (PanacheQueryException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Unable to perform a projection\")) {\n        return find(query, params); // constructor expression already maps results\n    }\n    throw e;\n}","preventionTips":["Never combine 'select new' with .project()","Choose either constructor expressions or project(Dto.class), not both","Review generated queries for embedded select clauses before projecting"],"tags":["quarkus","panache","projection","hql"],"backgroundTag":"projection-conflict-select-new","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"}