# Pagination shows current page, total pages, and prevents invalid page numbers

- **Pattern:** `ab-001069` (`directory-search-discovery.pagination-url.pagination-ux`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001069
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001069)

## Why it matters

Pagination without visible progress context — no current page number, no total page count — leaves users navigating blind. Under ISO 25010:2011 reliability, an application that crashes or returns an unhandled error on an out-of-range page number has a reliability defect. The failure surface is trivially reachable: any user who manually edits `?page=999` in a URL, or who follows a stale link to a page that no longer exists, hits the boundary. A crash response (500, unhandled exception, blank page) signals to search engine crawlers and users alike that the application is fragile.

## Severity rationale

High because missing page context disorients users and out-of-bounds page requests that crash the application create a reliability defect visible to both users and crawlers.

## Remediation

Clamp the requested page to `[1, maxPage]` on the server before querying, and render page progress explicitly in `components/Pagination.tsx`.

```tsx
// Server: clamp before querying
const maxPage = Math.ceil(totalCount / pageSize)
const safePage = Math.min(Math.max(1, requestedPage), maxPage)

// components/Pagination.tsx
<span>Page {currentPage} of {totalPages}</span>
<button disabled={currentPage === 1} onClick={() => onPageChange(currentPage - 1)}>
  Previous
</button>
<button disabled={currentPage === totalPages} onClick={() => onPageChange(currentPage + 1)}>
  Next
</button>
```

Never pass an unclamped page number to `OFFSET` — out-of-range offsets return empty results silently or, worse, trigger database errors if the offset exceeds integer bounds.

## Detection

- **ID:** `pagination-ux`
- **Severity:** `high`
- **What to look for:** Enumerate all relevant files and Examine the pagination control (if present). Check whether it displays the current page number (e.g., "Page 3 of 10") and total page count. Try navigating to an invalid page number (e.g., manually edit the URL to `page=999`). Verify the application either shows an error, redirects to the last valid page, or displays an empty/no-results state gracefully (not a crash).
- **Pass criteria:** At least 1 implementation must be present. Pagination shows current page and total pages. Invalid page numbers are handled gracefully (error message, redirect to last page, or empty state — not a crash).
- **Fail criteria:** Pagination does not show current page or total pages, or invalid page numbers cause an error or unexpected behavior.
- **Skip (N/A) when:** Pagination is not implemented or results fit on a single page. Signal: no pagination control found or API returns all results without paging.
- **Detail on fail:** `"Pagination shows 'Next' and 'Previous' buttons but no page numbers or total page count."` or `"Navigating to page=999 shows an error or crashes the page."`
- **Remediation:** Implement clear pagination with bounds checking:

  ```tsx
  // components/Pagination.tsx
  export function Pagination({ currentPage, totalPages, onPageChange }) {
    const prevPage = Math.max(1, currentPage - 1)
    const nextPage = Math.min(totalPages, currentPage + 1)

    return (
      <div className="flex justify-center items-center gap-4">
        <button
          disabled={currentPage === 1}
          onClick={() => onPageChange(prevPage)}
        >
          Previous
        </button>
        <span>
          Page {currentPage} of {totalPages}
        </span>
        <button
          disabled={currentPage === totalPages}
          onClick={() => onPageChange(nextPage)}
        >
          Next
        </button>
      </div>
    )
  }

  // In your page component
  const maxPage = Math.ceil(totalCount / pageSize)
  const safePage = Math.min(Math.max(1, requestedPage), maxPage)
  if (safePage !== requestedPage) {
    // Optionally redirect to the valid page
  }
  ```

  Validate the requested page number on the server and return only valid pages.

## External references

- iso-25010 reliability

Taxons: user-experience, error-resilience

HTML version: https://auditbuffet.com/patterns/ab-001069
