cakephp/cakephp · warning · PageOutOfBoundsException

Page number ` ` could not be found.

Error message

Page number `%s` could not be found.

What it means

After fetching the items and counts, the paginator compares the requested page number (from request params) to the computed current page (total count / page size). If the user asked for a page beyond the last existing page, PageOutOfBoundsException is thrown; its message renders the requested page number.

Solutions

  1. Wrap paginate() in try/catch for PageOutOfBoundsException and redirect to page 1 (the standard CakePHP exception-renderer behavior).
  2. Validate/sanitize the requested page before paginating and clamp it to an available range when you can count results beforehand.
  3. In the exception handler, map PageOutOfBoundsException to a 404 so clients stop crawling invalid pages.
  4. Regenerate pagination links from the current paging params instead of persisting absolute page numbers.

Example fix

// before
$results = $paginator->paginate($query, $this->request->getQueryParams());
// after
try {
    $results = $paginator->paginate($query, $this->request->getQueryParams());
} catch (PageOutOfBoundsException $e) {
    $params = $this->request->getQueryParams();
    unset($params['page']);
    return $this->redirect(['?' => $params]);
}
Defensive patterns

Strategy: try-catch

Validate before calling

$page = max(1, (int)($params['page'] ?? 1));
if ($page > 10000) { // sanity cap
    $page = 1;
}
$params['page'] = $page;
$results = $paginator->paginate($query, $params);

Try / catch

try {
    $results = $paginator->paginate($query, $params);
} catch (\Cake\Datasource\Exception\PageOutOfBoundsException $e) {
    unset($params['page']);
    $results = $paginator->paginate($query, $params); // or redirect to page 1
}

Prevention

When it happens

Trigger: Requesting ?page=99 when only 3 pages of results exist; requesting page > 1 on an empty or filtered-down result set; stale pagination links after records were deleted; a page parameter supplied as a huge number.

Common situations: Users bookmarking deep pagination links that become invalid after data changes; bots crawling page=1..N indefinitely; after narrowing a search filter, the retained page number exceeds the new page count.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of cakephp/cakephp@1128eba9b0 (2026-09-12). Data as JSON: /api/errors/d8259cbca293b9b2. Report an issue: GitHub.

Appendix: source

Thrown at src/Datasource/Paging/NumericPaginator.php:257

        }

        assert(
            $target instanceof RepositoryInterface,
            'Pagination target must be an instance of `' . QueryInterface::class
                . '` or `' . RepositoryInterface::class . '`.',
        );

        $data = $this->extractData($target, $params, $settings);
        $query = $this->getQuery($target, $query, $data);

        $countQuery = clone $query;
        $items = $this->getItems($query, $data);
        $this->pagingParams['count'] = count($items);
        $this->pagingParams['totalCount'] = $this->getCount($countQuery, $data);

        $pagingParams = $this->buildParams($data);
        if ($pagingParams['requestedPage'] > $pagingParams['currentPage']) {
            throw new PageOutOfBoundsException([
                'requestedPage' => $pagingParams['requestedPage'],
                'pagingParams' => $pagingParams,
            ]);
        }

        return $this->buildPaginated($items, $pagingParams);
    }

    /**
     * Build paginated result set.
     *
     * @param \Cake\Datasource\ResultSetInterface<int, mixed> $items
     * @param array $pagingParams
     * @return \Cake\Datasource\Paging\PaginatedInterface<int, mixed>
     */
    protected function buildPaginated(ResultSetInterface $items, array $pagingParams): PaginatedInterface
    {
        return new PaginatedResultSet($items, $pagingParams);

View on GitHub (pinned to 1128eba9b0)