jamiepine/voicebox · warning · HTTPException

offset must be >= 0

Error message

offset must be >= 0

What it means

Returned as a 400 from GET /captures when the `offset` query param is negative. Offset must be >= 0 for SQL LIMIT/OFFSET to be valid; a negative offset would either error in the DB layer or behave unpredictably. The route validates this explicitly before calling list_captures.

Source

Thrown at backend/routes/captures.py:79

        raise HTTPException(status_code=500, detail=str(e))

    return models.CaptureCreateResponse(
        **capture.model_dump(),
        auto_refine=bool(saved.auto_refine),
        allow_auto_paste=bool(saved.allow_auto_paste),
    )


@router.get("/captures", response_model=models.CaptureListResponse)
async def list_captures_endpoint(
    limit: int = 50,
    offset: int = 0,
    db: Session = Depends(get_db),
):
    if limit < 1 or limit > 200:
        raise HTTPException(status_code=400, detail="limit must be between 1 and 200")
    if offset < 0:
        raise HTTPException(status_code=400, detail="offset must be >= 0")

    items, total = captures_service.list_captures(db, limit=limit, offset=offset)
    return models.CaptureListResponse(items=items, total=total)


@router.get("/captures/{capture_id}", response_model=models.CaptureResponse)
async def get_capture_endpoint(capture_id: str, db: Session = Depends(get_db)):
    capture = captures_service.get_capture(capture_id, db)
    if not capture:
        raise HTTPException(status_code=404, detail="Capture not found")
    return capture


@router.get("/captures/{capture_id}/audio")
async def get_capture_audio_endpoint(capture_id: str, db: Session = Depends(get_db)):
    """Stream the original capture audio file."""
    row = db.query(DBCapture).filter(DBCapture.id == capture_id).first()
    if not row:

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Clamp offset to max(0, computed) on the client.
  2. Disable the 'previous' control when already on the first page so offset never goes negative.
  3. Use 1-based page numbers and compute offset = (page-1)*limit after guarding page>=1.

Example fix

// before
fetch(`/captures?offset=${page * limit - limit}`) // page 0 -> negative
// after
const offset = Math.max(0, (page - 1) * limit);
fetch(`/captures?offset=${offset}`)
Defensive patterns

Strategy: validation

Validate before calling

const offset = Math.max(0, Number(reqOffset) || 0);
fetch(`/captures?offset=${offset}`);

Type guard

function isValidOffset(n: number): boolean {
  return Number.isInteger(n) && n >= 0;
}

Prevention

When it happens

Trigger: GET /captures?offset=-1 or any negative integer for offset. Triggered by a client computing offset as (page-1)*limit with page<=0, or a decrement-past-zero bug in a 'previous page' control.

Common situations: Pagination 'previous' button on page 1 computing offset = -limit; a script decrementing offset below zero.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/77be34ca3dc16f90. Report an issue: GitHub.