jamiepine/voicebox · warning · HTTPException

File too large. Maximum size is {MAX_FILE_SIZE / (1024 * 102

Error message

File too large. Maximum size is {MAX_FILE_SIZE / (1024 * 1024)}MB

What it means

Returned by POST /history/import when `len(content) > MAX_FILE_SIZE` where MAX_FILE_SIZE is hardcoded to 50 * 1024 * 1024 (50 MB). The entire upload is read into memory via `await file.read()` before the check, then rejected with HTTP 400 (note: the status code is 400, though semantically 413 would be more appropriate). This endpoint imports a previously-exported generation ZIP.

Source

Thrown at backend/routes/history.py:52

@router.get("/history/stats")
async def get_stats(db: Session = Depends(get_db)):
    """Get generation statistics."""
    return await history.get_generation_stats(db)


@router.post("/history/import")
async def import_generation(
    file: UploadFile = File(...),
    db: Session = Depends(get_db),
):
    """Import a generation from a ZIP archive."""
    MAX_FILE_SIZE = 50 * 1024 * 1024

    content = await file.read()

    if len(content) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=400, detail=f"File too large. Maximum size is {MAX_FILE_SIZE / (1024 * 1024)}MB"
        )

    try:
        result = await export_import.import_generation_from_zip(content, db)
        return result
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@router.delete("/history/failed")
async def clear_failed_generations(db: Session = Depends(get_db)):
    """Delete every generation with status='failed'. Used by the UI's 'Clear failed' button (#410)."""
    count = await history.delete_failed_generations(db)
    return {"deleted": count}

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Ensure the imported ZIP is under 50 MB — if the contained audio is large, the export ZIP will exceed this.
  2. If larger imports are needed, raise MAX_FILE_SIZE in backend/routes/history.py and the reverse proxy body limit accordingly.
  3. Consider changing the status code to 413 for semantic correctness if you touch this code.
  4. Stream-check the size during read instead of after full buffering to avoid holding oversized payloads in memory.

Example fix

# before
MAX_FILE_SIZE = 50 * 1024 * 1024
if len(content) > MAX_FILE_SIZE:
    raise HTTPException(status_code=400, detail=f"File too large...")

# after
MAX_FILE_SIZE = 50 * 1024 * 1024
if len(content) > MAX_FILE_SIZE:
    raise HTTPException(status_code=413, detail=f"File too large...")
Defensive patterns

Strategy: validation

Validate before calling

const MAX = 50 * 1024 * 1024;
if (file.size > MAX) { alert(`ZIP exceeds ${MAX/1048576} MB`); return; }
await upload(file);

Type guard

function withinImportZipLimit(file, limitBytes = 50 * 1024 * 1024) {
  return file && typeof file.size === 'number' && file.size <= limitBytes;
}

Try / catch

try {
  const res = await fetch('/history/import', { method:'POST', body: form });
  if (res.status === 400 && /too large/i.test((await res.json()).detail)) {
    alert('Import ZIP too large'); return;
  }
} catch (e) { console.error(e); }

Prevention

When it happens

Trigger: Uploading a generation ZIP larger than 50 MB; the entire file is already buffered in memory by the time the size is checked, so a 100 MB upload wastes RAM before being rejected.

Common situations: Re-importing a generation whose embedded audio is large; exporting+re-importing after the audio was upscaled; a ZIP bomb or maliciously large archive.

Related errors


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