{"record":{"id":"5c33e4d3f21a760b","repo":"jamiepine/voicebox","slug":"file-too-large-maximum-size-is-max-file-size-5c33e4","errorCode":null,"errorMessage":"File too large. Maximum size is {MAX_FILE_SIZE / (1024 * 1024)}MB","messagePattern":"File too large\\. Maximum size is (.+?)MB","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"backend/routes/profiles.py","lineNumber":56,"sourceCode":"\n@router.get(\"/profiles\", response_model=list[models.VoiceProfileResponse])\nasync def list_profiles(db: Session = Depends(get_db)):\n    \"\"\"List all voice profiles.\"\"\"\n    return await profiles.list_profiles(db)\n\n\n@router.post(\"/profiles/import\", response_model=models.VoiceProfileResponse)\nasync def import_profile(\n    file: UploadFile = File(...),\n    db: Session = Depends(get_db),\n):\n    \"\"\"Import a voice profile from a ZIP archive.\"\"\"\n    MAX_FILE_SIZE = 100 * 1024 * 1024\n\n    content = await file.read()\n\n    if len(content) > MAX_FILE_SIZE:\n        raise HTTPException(\n            status_code=400, detail=f\"File too large. Maximum size is {MAX_FILE_SIZE / (1024 * 1024)}MB\"\n        )\n\n    try:\n        profile = await export_import.import_profile_from_zip(content, db)\n        return profile\n    except ValueError as e:\n        raise HTTPException(status_code=400, detail=str(e))\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n\n\n# ── Preset Voice Endpoints ───────────────────────────────────────────\n# These MUST be declared before /profiles/{profile_id} to avoid the\n# wildcard swallowing \"presets\" as a profile_id.\n\n\n@router.get(\"/profiles/presets/{engine}\")","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/profiles.py#L38-L74","documentation":"400 from POST /profiles/import. The endpoint reads the entire uploaded ZIP into memory (content = await file.read()) and rejects it if len(content) > MAX_FILE_SIZE where MAX_FILE_SIZE = 100 * 1024 * 1024 (100 MiB). The check happens after the full read, so the upload completes before the rejection — there's no streaming/chunked size guard at this layer.","triggerScenarios":"POST /profiles/import (multipart form field 'file') with a ZIP larger than 100 MiB; a profile whose cloned-voice samples were bundled into a very large archive; accidentally attaching an uncompressed export or the wrong file (e.g. a full backup dump).","commonSituations":"Profile with many long reference-audio samples; uncompressed ZIP (samples are already compressed audio, so ZIP gives little gain); user attached the whole data/ directory export by mistake; export from a system with higher per-profile sample limits imported on one with the 100 MiB cap.","solutions":["Reduce the archive below 100 MiB: remove unused samples, downsample/trim long clips, or re-export as a tighter archive.","Split a large multi-profile set into separate single-profile ZIPs and import each.","Confirm you're uploading a Voicebox profile export, not a generic backup.","If you genuinely need larger imports, raise MAX_FILE_SIZE server-side (and ensure reverse-proxy body limits — nginx client_max_body_size, etc. — are raised to match)."],"exampleFix":"# before\nPOST /profiles/import  with 180MB voicebox.zip  -> 400\n# after\nzip -9 voicebox.zip profile.json samples/   # recompress / trim to <100MB\nPOST /profiles/import  with voicebox.zip","handlingStrategy":"validation","validationCode":"const MAX_PROFILE_IMPORT_BYTES = 100 * 1024 * 1024;\nasync function importProfile(file: File) {\n  if (file.size > MAX_PROFILE_IMPORT_BYTES) {\n    throw new Error(`File too large (${(file.size/1048576).toFixed(1)}MB). Max is 100MB.`);\n  }\n  const form = new FormData();\n  form.append('file', file);\n  return await fetch('/profiles/import', {method:'POST', body: form});\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importProfile(file);\n} catch (e) {\n  if (e.response?.status === 400 && /File too large/i.test(e.response.detail)) {\n    // prompt user to trim the archive / split profiles\n  } else throw e;\n}","preventionTips":["Check file.size on the client before reading/uploading — the server reads the whole body first.","Strip unused samples and recompress exports before importing.","Align any reverse-proxy body limit (nginx client_max_body_size) with the 100 MiB cap."],"tags":["profiles","import","upload","size-limit","http-400","multipart"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}