{"record":{"id":"3988ed7cf6293adc","repo":"Comfy-Org/ComfyUI","slug":"heygen-returned-no-video-url-for-video-video-id","errorCode":null,"errorMessage":"HeyGen returned no video_url for video {video_id}.","messagePattern":"HeyGen returned no video_url for video (.+?)\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_api_nodes/nodes_heygen.py","lineNumber":88,"sourceCode":"    \"\"\"POST a /v3/videos payload, poll until terminal, and return the final video data.\"\"\"\n    created = await sync_op_raw(\n        cls,\n        ApiEndpoint(path=_VIDEOS_PATH, method=\"POST\", headers={\"Idempotency-Key\": uuid.uuid4().hex}),\n        data=payload,\n    )\n    video_id = (created.get(\"data\") or {}).get(\"video_id\")\n    if not video_id:\n        raise ValueError(f\"HeyGen did not return a video_id: {created}\")\n    final = await poll_op_raw(\n        cls,\n        ApiEndpoint(path=f\"{_VIDEOS_PATH}/{video_id}\"),\n        status_extractor=lambda r: (r.get(\"data\") or {}).get(\"status\"),\n        queued_statuses=[\"pending\", \"waiting\"],\n        poll_interval=5.0,\n    )\n    data = final[\"data\"]\n    if not data.get(\"video_url\"):\n        raise ValueError(f\"HeyGen returned no video_url for video {video_id}.\")\n    return data\n\n\nasync def _resolve_avatar(\n    cls: type[IO.ComfyNode], avatar_label: str, custom_avatar_id: str, engine_choice: str\n) -> tuple[str, str | None]:\n    \"\"\"Resolve (avatar_id, engine_type) from the combo/override + engine widgets.\"\"\"\n    custom_avatar_id = custom_avatar_id.strip()\n    if custom_avatar_id:\n        look = (\n            await sync_op_raw(\n                cls,\n                ApiEndpoint(path=f\"{_LOOKS_PATH}/{custom_avatar_id}\"),\n                final_label_on_success=None,\n            )\n        ).get(\"data\") or {}\n        avatar_id = custom_avatar_id\n        avatar_label = look.get(\"name\") or custom_avatar_id","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api_nodes/nodes_heygen.py#L70-L106","documentation":"Thrown when polling of a HeyGen video job reaches a terminal state but the final data object lacks video_url. The id existed and polling completed, yet the result payload is incomplete — typically a 'completed' job whose render artifact failed to publish, or a failed status the poller treated as final.","triggerScenarios":"poll_op_raw on GET /v3/videos/{video_id} returns data with no video_url; e.g. HeyGen marks the job failed or returns partial data after server-side render errors.","commonSituations":"HeyGen-side render failures (bad avatar/photo combination, unsupported resolution), transient CDN publishing issues, or jobs cancelled server-side.","solutions":["Retry the node; transient render/publish failures usually succeed on a new job (a fresh Idempotency-Key is generated per run)","Simplify the payload (different avatar, default resolution) to rule out render-side rejection","Check the video_id in the HeyGen dashboard for the detailed failure reason","If persistent across inputs, report upstream — video_id existed but no artifact was produced"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(2):\n    try:\n        return await _create_and_poll_video(cls, payload)\n    except ValueError as e:\n        if 'no video_url' in str(e) and attempt == 0:\n            continue  # transient render/publish failure; fresh idempotency key per run\n        raise","preventionTips":["Wrap HeyGen render calls in one retry — export-stage failures are usually transient","Check the job id in the HeyGen dashboard when a retry also fails"],"tags":["heygen","render-failure","polling","video-url"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}