{"record":{"id":"44e2096f1ed43892","repo":"Comfy-Org/ComfyUI","slug":"heygen-did-not-return-a-video-id-created","errorCode":null,"errorMessage":"HeyGen did not return a video_id: {created}","messagePattern":"HeyGen did not return a video_id: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy_api_nodes/nodes_heygen.py","lineNumber":78,"sourceCode":"        if voice_id:\n            payload[\"voice_id\"] = voice_id\n        elif require_voice:\n            raise ValueError(\"A voice is required when driving the video with a text script.\")\n        speed = speech.get(\"voice_speed\", 1.0)\n        if speed != 1.0:\n            payload[\"voice_settings\"] = {\"speed\": round(speed, 2)}\n\n\nasync def _create_and_poll_video(cls: type[IO.ComfyNode], payload: dict) -> dict:\n    \"\"\"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()","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy_api_nodes/nodes_heygen.py#L60-L96","documentation":"Thrown after POSTing to HeyGen's /v3/videos endpoint when the response JSON has no data.video_id. This is a protocol/contract failure or an error payload rather than a normal creation: the node cannot poll a job without an id, so it surfaces the entire response body for diagnosis.","triggerScenarios":"HeyGen returns an error object (auth failure, quota exceeded, invalid avatar_id, malformed payload) instead of a creation response; or an API version change moved/renamed video_id in the response schema.","commonSituations":"Expired/invalid HeyGen API credentials, out-of-credit account, deleted or wrong avatar_id, or upstream API schema changes after a HeyGen release.","solutions":["Read the embedded response in the message; it usually contains HeyGen's actual error (e.g. 401/402/invalid avatar)","Verify HeyGen account status and credits in the ComfyAPI partner settings","Confirm the avatar_id/voice_id used exist in your HeyGen workspace","If the payload looks correct and credentials are valid, report as an upstream API contract change"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    data = await _create_and_poll_video(cls, payload)\nexcept ValueError as e:\n    if 'did not return a video_id' in str(e):\n        # inspect embedded response: auth/quota/avatar errors land here\n        raise","preventionTips":["Verify HeyGen credentials and credits before batch runs","Validate avatar_id/voice_id exist before submitting payloads"],"tags":["heygen","api-contract","video-creation","polling"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}