quarkusio/quarkus · error · PanacheQueryException
Unable to perform a projection on a 'select [distinct]? new'
Error message
Unable to perform a projection on a 'select [distinct]? new' query: ${query} What it means
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.
Source
Thrown at extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java:138
// Builder
public CommonPanacheQueryImpl<Entity> sort(Sort sort) {
this.sort = sort;
return this;
}
public <T> CommonPanacheQueryImpl<T> project(Class<T> type) {
String selectQuery = query;
if (PanacheJpaUtil.isNamedQuery(query)) {
SelectionQuery<?> q = session.createNamedSelectionQuery(query.substring(1));
selectQuery = getQueryString(q);
}
String lowerCasedTrimmedQuery = PanacheJpaUtil.trimForAnalysis(selectQuery);
if (lowerCasedTrimmedQuery.startsWith("select new ")
|| lowerCasedTrimmedQuery.startsWith("select distinct new ")) {
throw new PanacheQueryException("Unable to perform a projection on a 'select [distinct]? new' query: " + query);
}
// If the query starts with a select clause, we pass it on to ORM which can handle that via a projection type
if (lowerCasedTrimmedQuery.startsWith("select ")) {
// I think projections do not change the result count, so we can keep the custom count query
return new CommonPanacheQueryImpl<>(this, query, customCountQueryForSpring, type);
}
// FIXME: this assumes the query starts with "FROM " probably?
// build select clause with a constructor expression
AtomicReference<String> cachedProjection = ProjectionQueryCache.get(type);
if (cachedProjection.get() == null) {
cachedProjection.set("SELECT " + getParametersFromClass(type, null));
}
String selectClause = cachedProjection.get();
// I think projections do not change the result count, so we can keep the custom count query
return new CommonPanacheQueryImpl<>(this, selectClause + selectQuery, customCountQueryForSpring, null);View on GitHub (pinned to e1c734241f)
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
Example fix
// before
PanacheQuery<Person> q = Person.find("select new com.acme.PersonView(p.name) from Person p").project(PersonView.class);
// after
PanacheQuery<Person> q = Person.find("select p.name from Person p").project(PersonView.class); Defensive patterns
Strategy: validation
Validate before calling
String lc = query.toLowerCase().trim();
if (lc.startsWith("select new ") || lc.startsWith("select distinct new ")) {
throw new IllegalArgumentException("Remove .project(); query already uses a constructor expression");
} Try / catch
try {
return find(query, params).project(dtoClass);
} catch (PanacheQueryException e) {
if (e.getMessage() != null && e.getMessage().contains("Unable to perform a projection")) {
return find(query, params); // constructor expression already maps results
}
throw e;
} Prevention
- 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
When it happens
Trigger: Calling .project(SomeDto.class) on a query string that begins with 'select new ' or 'select distinct new '.
Common situations: 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.
Related errors
- Unable to perform a projection on a 'select [distinct]? new'
- Unable to load repository/entity class mapping ->
- Your repository class was not properly detected and assigne
- Entity '%s' was not found. Did you forget to annotate your P
- Sort cannot be used with named query, add an "order by" clau
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/f35b278c48ff3df7.
Report an issue: GitHub.