{"record":{"id":"22ce10ce188430b2","repo":"jamiepine/voicebox","slug":"e-22ce10","errorCode":null,"errorMessage":"{e}","messagePattern":"\\{e\\}","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"backend/routes/models.py","lineNumber":60,"sourceCode":"                copied_so_far,\n                total_bytes,\n                filename=item.name,\n                status=\"downloading\",\n            )\n    return copied_so_far\n\n\n@router.post(\"/models/load\")\nasync def load_model(model_size: str = \"1.7B\"):\n    \"\"\"Manually load TTS model.\"\"\"\n    from ..services import tts\n\n    try:\n        tts_model = tts.get_tts_model()\n        await tts_model.load_model_async(model_size)\n        return {\"message\": f\"Model {model_size} loaded successfully\"}\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n\n\n@router.post(\"/models/unload\")\nasync def unload_model():\n    \"\"\"Unload the default Qwen TTS model to free memory.\"\"\"\n    from ..services import tts\n\n    try:\n        tts.unload_tts_model()\n        return {\"message\": \"Model unloaded successfully\"}\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n\n\n@router.post(\"/models/{model_name}/unload\")\nasync def unload_model_by_name(model_name: str):\n    \"\"\"Unload a specific model from memory without deleting it from disk.\"\"\"\n    from ..backends import get_model_config, unload_model_by_config","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/routes/models.py#L42-L78","documentation":"500 from POST /models/load. The endpoint calls tts.get_tts_model().load_model_async(model_size) inside a bare except Exception and re-raises with detail=str(e) — i.e. the raw exception text is forwarded to the client. model_size defaults to '1.7B' (a query parameter, not a JSON body). Failure modes include unsupported size for the current backend, network/HuggingFace download errors, VRAM exhaustion, and weight-loading failures.","triggerScenarios":"POST /models/load?model_size=7B (unsupported size); load while offline and the size isn't cached; load a size that exceeds available VRAM; concurrent load already in progress; mlx backend asked for a pytorch-only size.","commonSituations":"First-run download on a flaky connection; wrong default size after a backend switch (mlx vs pytorch); user manually invoked load on a size the registry doesn't expose; disk full in the HF cache.","solutions":["Read the detail string — this endpoint leaks the underlying message, which usually names the real cause (size unsupported, network, OOM).","Call GET /models/status to see which sizes are valid and already cached before loading.","Free memory first with POST /models/unload, then retry the load.","Confirm the model_size matches a registry value (e.g. 0.6B/1.7B/4B for Qwen TTS) and that your backend type supports it."],"exampleFix":"# before\ncurl -X POST 'http://localhost:8000/models/load?model_size=7B'\n# after\ncurl -X POST 'http://localhost:8000/models/load?model_size=1.7B'","handlingStrategy":"validation","validationCode":"const VALID_TTS_SIZES = ['0.6B','1.7B','4B']; // confirm against GET /models/status\nasync function loadTts(size: string) {\n  if (!VALID_TTS_SIZES.includes(size)) throw new Error(`Unsupported size: ${size}`);\n  const res = await fetch(`/models/load?model_size=${encodeURIComponent(size)}`, {method:'POST'});\n  if (!res.ok) {\n    const err = await res.json().catch(() => ({}));\n    throw new Error(`Load failed: ${err.detail ?? res.status}`);\n  }\n  return res.json();\n}","typeGuard":null,"tryCatchPattern":"try {\n  await loadTts('1.7B');\n} catch (e) {\n  // e.message contains the leaked detail — inspect for OOM/network/unsupported-size\n  if (/out of memory|VRAM|CUDA/i.test(e.message)) { await fetch('/models/unload',{method:'POST'}); await loadTts('0.6B'); }\n  else throw e;\n}","preventionTips":["Always confirm the size is supported for your backend_type via /models/status before loading.","Unload before loading a different size to avoid VRAM contention.","Remember this endpoint leaks the underlying error text — don't log it where users can read sensitive paths."],"tags":["models","tts","load","http-500","leaks-detail","qwen"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}