calesthio/OpenMontage · error · TimeoutError
HeyGen execution {execution_id} timed out after {timeout}s
Error message
HeyGen execution {execution_id} timed out after {timeout}s What it means
Raised as TimeoutError when the HeyGen execution poller exhausts its timeout budget (poll interval grows from a base via *1.2 backoff, capped at 30s) without the status becoming completed/failed/error. HeyGen renders can simply take longer than the configured timeout, especially for long multi-scene videos, so this is a client-side give-up, not a server-side failure.
Source
Thrown at tools/video/_shared.py:415
data = response.json().get("data", {})
status = data.get("status", "")
if status == "completed":
video_url = (
data.get("output", {}).get("video", {}).get("video_url")
or data.get("output", {}).get("video_url")
)
if video_url:
return video_url
raise RuntimeError(f"Completed but no video_url in output: {data}")
if status in {"failed", "error"}:
raise RuntimeError(f"HeyGen generation failed: {data.get('error', 'Unknown')}")
time.sleep(min(interval, max(0.0, deadline - time.time())))
interval = min(interval * 1.2, 30.0)
raise TimeoutError(f"HeyGen execution {execution_id} timed out after {timeout}s")
def upload_image_fal(image_path: str) -> str:
"""Upload a local image to fal.ai storage and return a public URL."""
import requests
api_key = os.environ.get("FAL_KEY") or os.environ.get("FAL_AI_API_KEY")
if not api_key:
raise RuntimeError("FAL_KEY or FAL_AI_API_KEY required for image upload")
path = Path(image_path)
if not path.exists():
raise FileNotFoundError(f"Image not found: {image_path}")
suffix = path.suffix.lower()
content_type = {"png": "image/png", "jpg": "image/jpeg", "jpeg": "image/jpeg", "webp": "image/webp"}.get(
suffix.lstrip("."), "image/png"
)View on GitHub (pinned to 95e1c3d0ab)
Solutions
- Increase the timeout parameter passed to the poll/exec call — HeyGen Video Agent jobs routinely need 10+ minutes
- Before assuming failure, GET the execution once more by id: it often completed after the caller gave up; reuse that URL instead of resubmitting (and paying) again
- Reduce scene count / video length if timeouts are budget-constrained
- Wrap with retry that first checks execution status by id, resubmitting only if actually failed
Example fix
# before video_url = wait_for_video(execution_id, timeout=180) # after video_url = wait_for_video(execution_id, timeout=900) # 15 min for long jobs
Defensive patterns
Strategy: retry
Validate before calling
null
Try / catch
try:
url = wait_for_video(execution_id, timeout=900)
except TimeoutError:
# poll once more by id: job frequently completed after give-up
data = fetch_execution(execution_id)
if data.get("status") == "completed":
return extract_video_url(data)
raise Prevention
- Scale timeout to job size (multi-scene Video Agent: 10+ minutes)
- Always keep the execution_id so a post-timeout status check can recover the asset without rebilling
- Use exponential poll intervals to reduce API pressure on long jobs
When it happens
Trigger: Rendering a multi-minute multi-scene video with a short timeout; HeyGen under heavy load with slow queue times; conservative timeout defaults (often far below real render durations for Video Agent jobs).
Common situations: Prompt-to-video agent jobs that take 5-15+ minutes while the caller allowed 2-3; peak-load days; 4K or many-scene compositions.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Prediction {prediction_id} did not finish within {timeout:.0
- Timed out waiting for Gemini Omni video to become ACTIVE
- Kling Classic task {task_id} timed out after {timeout_second
- Kling Turbo task {task_id} timed out after {timeout_seconds}
- DashScope ASR task {task_id} did not finish within {timeout_
AI-assisted analysis of calesthio/OpenMontage@95e1c3d0ab (2026-08-15).
Data as JSON: /api/errors/826586203e0508e9.
Report an issue: GitHub.