{"record":{"id":"5fd196f431119c3a","repo":"jamiepine/voicebox","slug":"file-size-exceeds-maximum-of-5mb","errorCode":null,"errorMessage":"File size exceeds maximum of 5MB","messagePattern":"File size exceeds maximum of 5MB","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"backend/services/profiles.py","lineNumber":649,"sourceCode":") -> VoiceProfileResponse:\n    \"\"\"\n    Upload and process avatar image for a profile.\n\n    Args:\n        profile_id: Profile ID\n        image_path: Path to uploaded image file\n        db: Database session\n\n    Returns:\n        Updated profile\n    \"\"\"\n    profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()\n    if not profile:\n        raise ValueError(f\"Profile {profile_id} not found\")\n\n    is_valid, error_msg = validate_image(image_path)\n    if not is_valid:\n        raise ValueError(error_msg)\n\n    if profile.avatar_path:\n        old_avatar = config.resolve_storage_path(profile.avatar_path)\n        if old_avatar is not None and old_avatar.exists():\n            old_avatar.unlink()\n\n    # Determine file extension from uploaded file\n    from PIL import Image\n\n    with Image.open(image_path) as img:\n        # Normalize JPEG variants (MPO is multi-picture format from some cameras)\n        img_format = img.format\n        if img_format in (\"MPO\", \"JPG\"):\n            img_format = \"JPEG\"\n\n        ext_map = {\"PNG\": \".png\", \"JPEG\": \".jpg\", \"WEBP\": \".webp\"}\n        ext = ext_map.get(img_format, \".png\")\n","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/services/profiles.py#L631-L667","documentation":"Returned by validate_image() in backend/utils/images.py when path.stat().st_size exceeds MAX_FILE_SIZE (5 * 1024 * 1024 bytes). upload_avatar() calls validate_image and re-raises the returned message as a ValueError. The cap protects avatar storage and downstream resize processing from oversized inputs.","triggerScenarios":"Uploading an avatar larger than 5 MB; high-resolution phone photos (often 5-15 MB JPEGs) used directly without pre-compression; animated/lossless PNG exports from design tools.","commonSituations":"Mobile uploads of full-camera-resolution photos; users exporting PNGs from Photoshop/Figma; GIF/BMP converted to large PNGs; HEIC converted to JPEG at very high quality.","solutions":["Compress or downscale the image to under 5 MB before uploading (export at <=1024px, JPEG quality ~85).","Raise the client-side size check so users are warned before the request.","If business needs warrant, increase MAX_FILE_SIZE in backend/utils/images.py and adjust any reverse-proxy body-size limit.","Convert HEIC/RAW sources to optimized JPEG first."],"exampleFix":"// before: uploading a 12MB JPEG straight from the camera\n// after: resize/compress client-side to <=5MB\nfrom PIL import Image\nimg = Image.open('avatar.jpg')\nimg.thumbnail((1024, 1024))\nimg.save('avatar_small.jpg', 'JPEG', quality=85)  // now well under 5MB","handlingStrategy":"validation","validationCode":"from backend.utils.images import MAX_FILE_SIZE\nfrom pathlib import Path\n\nif Path(image_path).stat().st_size > MAX_FILE_SIZE:\n    raise HTTPException(413, f\"Avatar exceeds {MAX_FILE_SIZE // (1024*1024)}MB\")","typeGuard":null,"tryCatchPattern":"try:\n    profile = await upload_avatar(profile_id, image_path, db)\nexcept ValueError as e:\n    if 'File size exceeds' in str(e):\n        raise HTTPException(413, str(e))\n    raise","preventionTips":["Enforce a client-side size check and warn before upload.","Configure the reverse proxy (nginx client_max_body_size) consistently with MAX_FILE_SIZE.","Guide users to downscale camera photos before upload."],"tags":["avatar","image","file-size","validation","voicebox"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}