{"record":{"id":"d9570b261c180f4c","repo":"jamiepine/voicebox","slug":"model-model-size-is-not-downloaded-yet-use-gen","errorCode":null,"errorMessage":"Model {model_size} is not downloaded yet. Use /generate to trigger a download.","messagePattern":"Model (.+?) is not downloaded yet\\. Use /generate to trigger a download\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/backends/__init__.py","lineNumber":554,"sourceCode":"        await backend.load_model()\n\n\nasync def ensure_model_cached_or_raise(engine: str, model_size: str = \"default\") -> None:\n    \"\"\"Check if a model is cached, raise HTTPException if not. Used by streaming endpoint.\"\"\"\n    from fastapi import HTTPException\n\n    backend = get_tts_backend_for_engine(engine)\n    cfg = None\n    for c in get_tts_model_configs():\n        if c.engine == engine and c.model_size == model_size:\n            cfg = c\n            break\n\n    if engine in (\"qwen\", \"qwen_custom_voice\", \"tada\"):\n        if not backend._is_model_cached(model_size):\n            raise HTTPException(\n                status_code=400,\n                detail=f\"Model {model_size} is not downloaded yet. Use /generate to trigger a download.\",\n            )\n    else:\n        if not backend._is_model_cached():\n            display = cfg.display_name if cfg else engine\n            raise HTTPException(\n                status_code=400,\n                detail=f\"{display} model is not downloaded yet. Use /generate to trigger a download.\",\n            )\n\n\ndef unload_model_by_config(config: ModelConfig) -> bool:\n    \"\"\"Unload a model given its config. Returns True if it was loaded, False otherwise.\"\"\"\n    from . import get_tts_backend_for_engine\n    from ..services import tts, transcribe, llm as llm_service\n\n    if config.engine == \"whisper\":\n        whisper_model = transcribe.get_whisper_model()\n        if whisper_model.is_loaded() and whisper_model.model_size == config.model_size:","sourceCodeStart":536,"sourceCodeEnd":572,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/backends/__init__.py#L536-L572","documentation":"The detail string of the same HTTPException as error 35 — the f-string `f\"Model {model_size} is not downloaded yet...\"` passed to detail= for qwen/qwen_custom_voice/tada engines when _is_model_cached(model_size) is False. Same root cause and recovery; the {model_size} placeholder interpolates the requested size so the client knows which size is missing.","triggerScenarios":"Streaming endpoint invoked for engine in {qwen, qwen_custom_voice, tada} with a model_size whose weights are absent from the cache directory; _is_model_cached(model_size) returns False.","commonSituations":"Client requests a size (e.g. '0.6B') before downloading it. Cache wiped. Multiple-size engine where only the default size was pre-fetched.","solutions":["Trigger the download via the /generate endpoint for the exact engine+model_size first.","Pre-fetch all needed sizes during onboarding.","Confirm the cache path and that _is_model_cached(model_size) detects the expected file layout.","List cached sizes in the response so clients can choose an available one."],"exampleFix":"# before\ndetail=f\"Model {model_size} is not downloaded yet. Use /generate to trigger a download.\"\n# after\ndetail=f\"Model '{model_size}' for engine '{engine}' is not downloaded yet. Use /generate to trigger a download.\"","handlingStrategy":"validation","validationCode":"def is_model_cached(engine: str, model_size: str) -> bool:\n    backend = get_tts_backend_for_engine(engine)\n    if engine in ('qwen', 'qwen_custom_voice', 'tada'):\n        return backend._is_model_cached(model_size)\n    return backend._is_model_cached()\n\n# Before streaming:\nif not is_model_cached(engine, model_size):\n    await load_engine_model(engine, model_size)","typeGuard":null,"tryCatchPattern":"from fastapi import HTTPException\ntry:\n    await ensure_model_cached_or_raise(engine, model_size)\nexcept HTTPException as e:\n    if e.status_code == 400:\n        await load_engine_model(engine, model_size)\n    else:\n        raise","preventionTips":["Check _is_model_cached(model_size) before the streaming call and trigger /generate when missing.","Keep model sizes consistent between the client request and the cache layout.","Pre-fetch non-default sizes the client may request."],"tags":["backend","python","fastapi","tts","model-cache","validation","http-400"],"backgroundTag":null,"analyzedSha":"51f49dea198384b4eb6087b72c17057c6eb1c1cd","analyzedAt":"2026-08-12T16:51:42.824Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}