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

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.

Source

Thrown at extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonAbstractPanacheQueryImpl.java:111

    protected abstract void disableFilter(SessionType session, String filter);

    // Builder

    public CommonAbstractPanacheQueryImpl<Entity, SessionType> sort(Sort sort) {
        this.sort = sort;
        return this;
    }

    public <T> CommonAbstractPanacheQueryImpl<T, SessionType> project(Class<T> type) {
        String selectQuery = query;
        if (PanacheJpaUtil.isNamedQuery(query)) {
            selectQuery = NamedQueryUtil.getNamedQuery(query.substring(1));
        }

        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 newQuery(query, customCountQueryForSpring, type);
        }

        // FIXME: this assumes the query starts with "FROM " probably?

        // build select clause with a constructor expression
        String selectClause = "SELECT " + getParametersFromClass(type, null);
        // I think projections do not change the result count, so we can keep the custom count query
        return newQuery(selectClause + selectQuery, customCountQueryForSpring, null);
    }

    private StringBuilder getParametersFromClass(Class<?> type, String parentParameter) {
        BiFunction<Class<?>, String, String> nestedProjectionBuilder = (nestedType, parameterName) -> getParametersFromClass(

View on GitHub (pinned to e1c734241f)

Solutions

  1. Remove the 'select new' clause and use a plain 'from Entity' query with .project(Dto.class)
  2. If you need constructor expressions, drop .project() and use the 'select new' query directly with the DTO as result type
  3. For named queries, keep the ORDER/simple select and project from the entity query

Example fix

// before
find("select new com.acme.Dto(p.name) from Person p").project(Dto.class)
// after
find("from Person p").project(Dto.class)
// or without project():
find("select new com.acme.Dto(p.name) from Person p").list()
Defensive patterns

Strategy: validation

Validate before calling

String q = namedQuery ? NamedQueryUtil.getNamedQuery(panacheQuery.substring(1)) : panacheQuery;
String lower = q.toLowerCase().trim();
if (lower.startsWith("select new ") || lower.startsWith("select distinct new "))
    throw new IllegalArgumentException("Cannot combine .project() with a 'select new' query");

Try / catch

try {
    return pq.project(Dto.class);
} catch (PanacheQueryException e) {
    if (e.getMessage() != null && e.getMessage().contains("select [distinct]? new")) {
        throw new IllegalArgumentException("Use the 'select new' query directly instead of .project()", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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().

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/148ba77ec691fdf9. Report an issue: GitHub.