{"record":{"id":"2f5462e427982c83","repo":"quarkusio/quarkus","slug":"cannot-navigate-to-last-page-in-cursor-based-pagin","errorCode":null,"errorMessage":"Cannot navigate to last page in cursor-based pagination","messagePattern":"Cannot navigate to last page in cursor-based pagination","errorType":"exception","errorClass":"UnsupportedOperationException","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":241,"sourceCode":"            page(page.previous());\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void firstPage() {\n        checkPagination();\n        if (keyedPage != null) {\n            keyedPage = keyedPage.getPage().first().keyedBy((List) keyedPage.getKeyDefinition());\n            lastKeyedResult = null;\n        } else {\n            page(page.first());\n        }\n    }\n\n    public void lastPage() {\n        checkPagination();\n        if (keyedPage != null) {\n            throw new UnsupportedOperationException(\n                    \"Cannot navigate to last page in cursor-based pagination\");\n        }\n        page(page.index(pageCount() - 1));\n    }\n\n    public boolean hasNextPage() {\n        checkPagination();\n        if (keyedPage != null) {\n            if (lastKeyedResult == null) {\n                throw new UnsupportedOperationException(\n                        \"Cannot call hasNextPage() before fetching results with list()\");\n            }\n            return !lastKeyedResult.isLastPage();\n        }\n        return page.index < (pageCount() - 1);\n    }\n\n    public boolean hasPreviousPage() {","sourceCodeStart":223,"sourceCodeEnd":259,"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#L223-L259","documentation":"In Panache, when a query is paginated with keyset (cursor-based) pagination via KeyedPage, the total page count is unknown because it would require counting the whole result set. Therefore lastPage(), which navigates by page index, cannot work and throws UnsupportedOperationException.","triggerScenarios":"Calling query.page(KeyedPage.of(...)) (or a keyed page variant) on a PanacheQuery/CommonPanacheQueryImpl and then calling lastPage().","commonSituations":"Developers switching from offset pagination to cursor-based pagination for large tables and keeping their old page-navigation code (firstPage/lastPage/nextPage by index) unchanged.","solutions":["Do not call lastPage() with cursor-based pagination; iterate forward with nextPage()/hasNextPage() instead.","Use offset pagination (query.page(pageIndex, pageSize)) if you truly need random-access navigation to the last page.","If you need the last items, sort descending and use firstPage() with the reversed sort."],"exampleFix":"// before\nquery.page(KeyedPage.of(KeyedPage.LAST, pageSize));\nquery.lastPage(); // throws\n\n// after\nquery.page(KeyedPage.of(KeyedPage.NEXT, pageSize));\nwhile (query.hasNextPage()) {\n    query.nextPage();\n}","handlingStrategy":"validation","validationCode":"if (queryIsKeyedPaginated) {\n    throw new IllegalArgumentException(\"lastPage() is unsupported with cursor-based pagination; use nextPage()/hasNextPage()\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    query.lastPage();\n} catch (UnsupportedOperationException e) {\n    // fall back to iterating forward or offset pagination\n}","preventionTips":["Never mix keyed pagination with index-based navigation helpers (firstPage/lastPage).","Encapsulate pagination strategy in one helper so keyed/offset differences are hidden.","Reverse the sort order instead of jumping to the last page."],"tags":["panache","pagination","cursor","unsupported-operation"],"backgroundTag":"cursor-pagination-no-random-access","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"}