ruvnet/RuView · warning · HTTPException

Page must be >= 1

Error message

Page must be >= 1

What it means

Raised as HTTP 400 by the PaginationParams dependency in archive/v1/src/api/dependencies.py:338 when the `page` query parameter is less than 1. The API is 1-based because the offset is computed as (page - 1) * size, so page < 1 would yield a negative offset. Since this check runs inside a FastAPI dependency, the request is rejected before any route handler executes.

Source

Thrown at archive/v1/src/api/dependencies.py:338

def get_router_config(router_id: str = Depends(validate_router_access)):
    """Get router configuration."""
    domain_config = get_domain_config()
    return domain_config.get_router(router_id)


# Pagination dependencies
class PaginationParams:
    """Pagination parameters."""
    
    def __init__(
        self,
        page: int = 1,
        size: int = 20,
        max_size: int = 100
    ):
        if page < 1:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Page must be >= 1"
            )
        
        if size < 1:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Size must be >= 1"
            )
        
        if size > max_size:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=f"Size must be <= {max_size}"
            )
        
        self.page = page
        self.size = size

View on GitHub (pinned to 4685618388)

Solutions

  1. Send page=1 — the first page is 1, not 0
  2. Clamp the value in the client before every request: page = max(1, currentPage)
  3. If you own the server, clamp inside the dependency instead of raising so legacy clients keep working

Example fix

// before
const res = await fetch(`/api/v1/detections?page=${pageIdx}&size=20`);

// after
const page = Math.max(1, pageIdx + 1); // UI index is 0-based, API is 1-based
const res = await fetch(`/api/v1/detections?page=${page}&size=20`);
Defensive patterns

Strategy: validation

Validate before calling

def build_pagination(page, size, max_size=100):
    page = max(1, int(page if page is not None else 1))
    size = max(1, min(int(size if size is not None else 20), max_size))
    return {'page': page, 'size': size}

params = build_pagination(ui_page, ui_size)

Type guard

def is_valid_page(page) -> bool:
    return isinstance(page, int) and not isinstance(page, bool) and page >= 1

Try / catch

resp = await client.get('/api/v1/detections', params=params)
if resp.status_code == 400 and 'Page must be' in resp.json().get('detail', ''):
    params['page'] = 1
    resp = await client.get('/api/v1/detections', params=params)
resp.raise_for_status()

Prevention

When it happens

Trigger: Calling any paginated list endpoint wired with Depends(get_pagination_params) while passing ?page=0 or a negative page, e.g. GET /api/v1/detections?page=0&size=20.

Common situations: A JS frontend forwards its 0-based UI index unchanged; a 'previous page' handler decrements below 1 on the first page; `page` is derived from an unset variable that defaults to 0 before being sent.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/3dc9175d9391b827. Report an issue: GitHub.