{"record":{"id":"30f5dc0396bde645","repo":"ATH-MaaS/Pixelle-Video","slug":"frame-template-is-required-to-determine-media-size","errorCode":null,"errorMessage":"frame_template is required to determine media size","messagePattern":"frame_template is required to determine media size","errorType":"exception","errorClass":"ValueError","httpStatus":500,"severity":"error","filePath":"api/routers/video.py","lineNumber":112,"sourceCode":"    \"\"\"\n    Generate video synchronously\n    \n    This endpoint blocks until video generation is complete.\n    Suitable for small videos (< 30 seconds).\n    \n    **Note**: May timeout for large videos. Use `/generate/async` instead.\n    \n    Request body includes all video generation parameters.\n    See VideoGenerateRequest schema for details.\n    \n    Returns path to generated video, duration, and file size.\n    \"\"\"\n    try:\n        logger.info(f\"Sync video generation: {request_body.text[:50]}...\")\n        \n        # Auto-determine media_width and media_height from template meta tags (required)\n        if not request_body.frame_template:\n            raise ValueError(\"frame_template is required to determine media size\")\n        \n        from pixelle_video.services.frame_html import HTMLFrameGenerator\n        from pixelle_video.utils.template_util import resolve_template_path\n        template_path = resolve_template_path(request_body.frame_template)\n        generator = HTMLFrameGenerator(template_path)\n        media_width, media_height = generator.get_media_size()\n        logger.debug(f\"Auto-determined media size from template: {media_width}x{media_height}\")\n        \n        # Build video generation parameters\n        video_params = {\n            \"text\": request_body.text,\n            \"mode\": request_body.mode,\n            \"title\": request_body.title,\n            \"n_scenes\": request_body.n_scenes,\n            \"min_narration_words\": request_body.min_narration_words,\n            \"max_narration_words\": request_body.max_narration_words,\n            \"min_image_prompt_words\": request_body.min_image_prompt_words,\n            \"max_image_prompt_words\": request_body.max_image_prompt_words,","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/ATH-MaaS/Pixelle-Video/blob/848b054e4fae40dabc62ec58e960b573e83793ac/api/routers/video.py#L94-L130","documentation":"The /video/generate/sync endpoint requires the request body to include frame_template because media_width and media_height are auto-detected from the template's HTML meta tags via HTMLFrameGenerator.get_media_size(). If frame_template is missing, null, or an empty string, generate_video_sync raises ValueError before any generation starts. This is an intentional fail-fast guard, not a bug.","triggerScenarios":"POST /api/video/generate/sync with a VideoGenerateRequest body that omits frame_template or sets it to null/empty string; the check at api/routers/video.py:111-112 fires before resolve_template_path is called.","commonSituations":"Clients built against an older API version where media_width/media_height were passed explicitly and frame_template was optional; hand-written curl/JSON payloads missing the field; SDK defaults that leave the field unset; copying a minimal example payload that predates the template-based sizing requirement.","solutions":["Add frame_template to the request body with the name of an existing template (e.g. \"default\"), letting the endpoint derive media size from its meta tags","Ensure the field is a non-empty string, not null or \"\" (falsy values are rejected)","Verify against the current VideoGenerateRequest schema (api/schemas/video.py) that frame_template is populated; update stale client code or saved request templates","Check available templates in the templates directory and confirm resolve_template_path can find the one you pass (a valid template is needed immediately after this check)"],"exampleFix":"// before\n{\"text\": \"Hello world\", \"mode\": \"standard\"}\n// after\n{\"text\": \"Hello world\", \"mode\": \"standard\", \"frame_template\": \"default\"}","handlingStrategy":"validation","validationCode":"def build_sync_request(body: dict) -> dict:\n    template = body.get(\"frame_template\")\n    if not template or not isinstance(template, str) or not template.strip():\n        raise ValueError(\"frame_template is required and must be a non-empty template name\")\n    return {**body, \"frame_template\": template.strip()}","typeGuard":"def has_frame_template(body: dict) -> bool:\n    return isinstance(body.get(\"frame_template\"), str) and bool(body[\"frame_template\"].strip())","tryCatchPattern":"try:\n    resp = requests.post(f\"{BASE}/api/video/generate/sync\", json=payload, timeout=300)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    detail = e.response.json().get(\"detail\", str(e))\n    if \"frame_template is required\" in detail:\n        payload[\"frame_template\"] = \"default\"\n        resp = requests.post(f\"{BASE}/api/video/generate/sync\", json=payload, timeout=300)\n    else:\n        raise","preventionTips":["Always set frame_template in every VideoGenerateRequest; treat it as mandatory","Validate the payload against the current VideoGenerateRequest schema before sending","Keep client SDKs/payload templates in sync with API version changes","Maintain a validated list of template names and pick from it, never free text"],"tags":["fastapi","validation","missing-parameter","video"],"backgroundTag":"missing-required-parameter","analyzedSha":"848b054e4fae40dabc62ec58e960b573e83793ac","analyzedAt":"2026-08-30T03:24:41.468Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}