{"record":{"id":"d583cf92d4f57d75","repo":"jamiepine/voicebox","slug":"display-model-is-not-downloaded-yet-use-genera","errorCode":null,"errorMessage":"{display} model 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":559,"sourceCode":"    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:\n            transcribe.unload_whisper_model()\n            return True\n        return False\n\n    if config.engine == \"qwen_llm\":","sourceCodeStart":541,"sourceCodeEnd":577,"githubUrl":"https://github.com/jamiepine/voicebox/blob/51f49dea198384b4eb6087b72c17057c6eb1c1cd/backend/backends/__init__.py#L541-L577","documentation":"Raised as HTTPException(400) by the else branch of ensure_model_cached_or_raise() for single-size engines (luxtts, chatterbox, chatterbox_turbo, kokoro) when _is_model_cached() (no arg) returns False. The detail uses cfg.display_name if a ModelConfig matched, otherwise the raw engine string, so the message names the human-readable model.","triggerScenarios":"Streaming endpoint called for a single-size engine whose weights are not on disk. cfg is found via get_tts_model_configs() matching engine+model_size; its display_name is interpolated. If no config matched (unknown model_size for that engine), `engine` itself is used.","commonSituations":"Fresh install without the chosen engine downloaded. User switched default engine in settings but never downloaded it. Cache directory moved/cleared. A config table mismatch means display_name falls back to the engine key.","solutions":["Run /generate once for that engine to trigger the download before streaming.","Download the engine via the model-management UI.","Confirm the engine's model config exists in get_tts_model_configs() so display_name (not the raw key) shows.","Verify the cache directory and _is_model_cached() detection logic for that engine."],"exampleFix":"# before\nraise HTTPException(status_code=400, detail=f\"{display} model is not downloaded yet. Use /generate to trigger a download.\")\n# after\nraise HTTPException(\n    status_code=400,\n    detail=f\"{display} model is not downloaded yet. Use /generate to trigger a download, or choose an installed engine.\",\n)","handlingStrategy":"validation","validationCode":"def single_size_engine_ready(engine: str) -> bool:\n    backend = get_tts_backend_for_engine(engine)\n    return backend._is_model_cached()\n\n# Before streaming for luxtts/chatterbox/kokoro:\nif not single_size_engine_ready(engine):\n    await load_engine_model(engine)","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)\n    else:\n        raise","preventionTips":["Ensure the engine's ModelConfig has a display_name so the message is human-readable.","Pre-download single-size engines via the model-management UI before streaming.","Verify the cache path and _is_model_cached() detection for each engine."],"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-12T18:17:37.767Z"}