jamiepine/voicebox · error · HTTPException

Capture not found

Error message

Capture not found

What it means

Returned as a 404 from GET /captures/{capture_id} when captures_service.get_capture returns a falsy value — no Capture row matches the id (or the service's response model validation produced None). The handler returns early before serializing. This is the canonical lookup-miss for a single capture.

Source

Thrown at backend/routes/captures.py:89

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:
        raise HTTPException(status_code=404, detail="Capture not found")

    audio_path = config.resolve_storage_path(row.audio_path)
    if audio_path is None or not audio_path.exists():
        raise HTTPException(status_code=404, detail="Audio file not found")

    return FileResponse(
        audio_path,
        media_type="audio/wav",
        filename=f"capture_{capture_id}.wav",

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Confirm capture_id against the current captures list.
  2. Query the DB directly: SELECT * FROM captures WHERE id = ?.
  3. If the capture was deleted, remove the stale reference from the client.
  4. If expecting a just-created capture, retry briefly to account for commit/replication lag.
Defensive patterns

Strategy: validation

Validate before calling

const list = await listCaptures();
if (!list.items.some(c => c.id === captureId)) {
  throw new Error('Capture id not in current list');
}

Type guard

function captureExists(id: string, items: { id: string }[]): boolean {
  return items.some(c => c.id === id);
}

Try / catch

try {
  const r = await fetch(`/captures/${id}`);
  if (r.status === 404) { dropStaleCapture(id); return; }
} catch (e) { showNetworkError(e); }

Prevention

When it happens

Trigger: GET /captures/{capture_id} with an id that does not exist: deleted capture, typo, id from another deployment, or a just-created id not yet committed/visible.

Common situations: Client holds a capture id from a previous session after the capture was deleted; race where the client polls an id before the creating transaction committed; cross-environment id leakage (staging id sent to prod).

Related errors


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