{"record":{"id":"e4b95bca9cd13f5a","repo":"Comfy-Org/ComfyUI","slug":"ltx-job-job-id-completed-without-a-video-url","errorCode":null,"errorMessage":"LTX job {job.id} completed without a video URL.","messagePattern":"LTX job (.+?) completed without a video URL\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"comfy_api_nodes/nodes_ltxv.py","lineNumber":78,"sourceCode":"    result: Ltx25JobResult | None = Field(None)\n\n\nasync def _v25_submit_and_poll(cls: type[IO.ComfyNode], route: str, data: BaseModel) -> IO.NodeOutput:\n    submit = await sync_op(\n        cls,\n        ApiEndpoint(f\"/proxy/ltx/v2/{route}\", \"POST\"),\n        response_model=Ltx25SubmitResponse,\n        data=data,\n        max_retries=1,\n    )\n    job = await poll_op(\n        cls,\n        ApiEndpoint(f\"/proxy/ltx/v2/{route}/{submit.id}\"),\n        response_model=Ltx25JobStatusResponse,\n        status_extractor=lambda r: r.status,\n    )\n    if not job.result or not job.result.video_url:\n        raise RuntimeError(f\"LTX job {job.id} completed without a video URL.\")\n    return IO.NodeOutput(await download_url_to_video_output(job.result.video_url, cls=cls))\n\n\nPRICE_BADGE = IO.PriceBadge(\n    depends_on=IO.PriceBadgeDepends(widgets=[\"model\", \"duration\", \"resolution\"]),\n    expr=\"\"\"\n    (\n      $prices := {\n        \"ltx-2 (pro)\": {\"1920x1080\":0.06,\"2560x1440\":0.12,\"3840x2160\":0.24},\n        \"ltx-2 (fast)\": {\"1920x1080\":0.04,\"2560x1440\":0.08,\"3840x2160\":0.16}\n      };\n      $modelPrices := $lookup($prices, $lowercase(widgets.model));\n      $pps := $lookup($modelPrices, widgets.resolution);\n      {\"type\":\"usd\",\"usd\": $pps * widgets.duration}\n    )\n    \"\"\",\n)\n","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api_nodes/nodes_ltxv.py#L60-L96","documentation":"Raised inside the LTX v2.5 submit-and-poll helper when a job reaches a finished state but job.result or job.result.video_url is falsy. The LTX task completed without producing a downloadable video, so the node cannot build a video output.","triggerScenarios":"_v25_submit_and_poll polling /proxy/ltx/v2/<route>/<id> ends with an Ltx25JobStatusResponse whose status is terminal but result.video_url is null/missing — server-side render failure, safety rejection, or truncated job record. Affects all v2.5 routes (text-to-video, image-to-video, audio-to-video).","commonSituations":"LTX render failing server-side under load; prompt flagged; job record shape changed after an API update so video_url lands elsewhere; rare cases where the poll's terminal status is 'failed' rather than 'succeeded'.","solutions":["Re-submit the task once; transient LTX render failures are the most frequent cause.","Inspect job.status semantics — if the poll returned 'failed', simplify the prompt and verify settings pass _v25_validate_settings.","Check LTX service status and your account's task history for the failed job id (in the error context).","Update comfy_api_nodes if the failure is consistent across prompts (response-model drift)."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"def ltx_job_has_video(job: Ltx25JobStatusResponse) -> bool:\n    return bool(job.result and job.result.video_url)","tryCatchPattern":"for attempt in range(2):\n    try:\n        return await _v25_submit_and_poll(cls, route, data)\n    except RuntimeError as e:\n        if attempt == 0 and \"without a video URL\" in str(e):\n            continue  # transient LTX render failure\n        raise","preventionTips":["Retry once before escalating; most empty-result jobs are transient.","Validate settings client-side (_v25_validate_settings) so server rejections stay rare.","Keep the job id available for LTX dashboard lookup."],"tags":["ltx","api-nodes","polling","remote-api","video"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}