quarkusio/quarkus · error · UnsupportedOperationException

Cannot use cursor-based pagination without sort criteria: us

Error message

Cannot use cursor-based pagination without sort criteria: use find(entityClass, query, sort) or findAll(entityClass, sort)

What it means

Cursor (keyset/keyed) pagination derives page boundaries from sorted column values; without at least one sort column the key is undefined. Panache throws UnsupportedOperationException when cursor(pageIndex, pageSize) is called on a query with no Sort or an empty Sort.

Source

Thrown at extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java:186

            filters = new HashMap<>();
        filters.put(filterName, parameters);
    }

    public void page(Page page) {
        this.page = page;
        this.range = null;
        this.keyedPage = null;
        this.lastKeyedResult = null;
    }

    public void page(int pageIndex, int pageSize) {
        page(Page.of(pageIndex, pageSize));
    }

    @SuppressWarnings("unchecked")
    public void cursor(int pageIndex, int pageSize) {
        if (sort == null || sort.getColumns().isEmpty()) {
            throw new UnsupportedOperationException(
                    "Cannot use cursor-based pagination without sort criteria: use find(entityClass, query, sort) or findAll(entityClass, sort)");
        }
        List orders = PanacheJpaUtil.toHibernateOrders(entityClass, sort);
        this.keyedPage = org.hibernate.query.Page.page(pageSize, pageIndex).keyedBy(orders);
        this.lastKeyedResult = null;
        this.page = null;
        this.range = null;
    }

    public void nextPage() {
        checkPagination();
        if (keyedPage != null) {
            if (lastKeyedResult == null) {
                throw new UnsupportedOperationException(
                        "Cannot call nextPage() before fetching results with list()");
            }
            keyedPage = lastKeyedResult.getNextPage();
            lastKeyedResult = null;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Pass a Sort with at least one stable unique column: findAll(Sort.ascending("id"))
  2. Ensure the Sort is not conditionally left empty at runtime
  3. Fall back to page() based offset pagination if no meaningful sort exists

Example fix

// before
Person.findAll().cursor(0, 20);
// after
Person.findAll(Sort.ascending("id")).cursor(0, 20);
Defensive patterns

Strategy: validation

Validate before calling

if (sort == null || sort.getColumns().isEmpty()) {
    throw new IllegalArgumentException("Cursor pagination requires a Sort with at least one column");
}

Type guard

boolean hasSort(io.quarkus.hibernate.orm.panache.common.Sort s) {
    return s != null && !s.getColumns().isEmpty();
}

Try / catch

try {
    query.cursor(pageIndex, pageSize).list();
} catch (UnsupportedOperationException e) {
    if (e.getMessage() != null && e.getMessage().contains("without sort criteria")) {
        // fall back to offset pagination or add a Sort
    } else throw e;
}

Prevention

When it happens

Trigger: Creating a PanacheQuery with find/findAll without a Sort (or Sort.empty()) and then calling .cursor(pageIndex, pageSize) or .cursor(Page).

Common situations: Adding cursor pagination to an existing findAll() call that never needed sorting; sort built conditionally so it ends up empty at runtime; upgrading Panache and switching from offset to cursor-based paging.

Related errors


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