NousResearch/hermes-agent · warning · TimeoutError

video job {getattr(video, 'id', '?')} did not reach a termin

Error message

video job {getattr(video, 'id', '?')} did not reach a terminal status within {int(self._poll_deadline_s)}s (last status={getattr(video, 'status', None)!r})

What it means

TimeoutError from the bounded polling loop in agent/video_gen_provider.py:440. This replaces client.videos.create_and_poll (which looped at 1/s forever) with a coarse _poll_interval_s sleep and a wall-clock deadline of _poll_deadline_s; if the job's status never enters the terminal set (completed/succeeded/failed/error/cancelled/canceled) before the deadline, this error names the job id and last observed status.

Source

Thrown at agent/video_gen_provider.py:440

    def is_available(self) -> bool:
        return bool(self._api_key())

    def _create_and_poll(self, client: Any, call_kwargs: Dict[str, Any]) -> Any:
        """Create the video job and poll to completion with a hard deadline.

        Replaces ``client.videos.create_and_poll`` (unbounded 1/s loop) with a
        coarse interval and a wall-clock cap. Returns the terminal video object
        (any status); raises :class:`TimeoutError` if the deadline passes
        first.
        """
        import time

        video = client.videos.create(**call_kwargs)
        terminal = {"completed", "succeeded", "failed", "error", "cancelled", "canceled"}
        deadline = time.monotonic() + self._poll_deadline_s
        while getattr(video, "status", None) not in terminal:
            if time.monotonic() >= deadline:
                raise TimeoutError(
                    f"video job {getattr(video, 'id', '?')} did not reach a terminal "
                    f"status within {int(self._poll_deadline_s)}s "
                    f"(last status={getattr(video, 'status', None)!r})"
                )
            time.sleep(self._poll_interval_s)
            video = client.videos.retrieve(video.id)
        return video

    def _base_url(self) -> str:
        import os

        override = os.environ.get(f"{self.name.upper()}_BASE_URL", "").strip()
        return override or self._default_base_url

    def generate(
        self,
        prompt: str,
        *,

View on GitHub (pinned to c896c09c42)

Solutions

  1. Increase the provider's poll deadline (_poll_deadline_s) to cover the worst-case queue time for the requested duration/resolution.
  2. Check the job id on the provider's dashboard — the error's (last status=...) tells you whether it was queued vs in-progress vs stalled.
  3. Treat this timeout as 'unknown outcome': the job may still complete and bill; verify on the provider side before regenerating.
  4. If jobs are systematically slower than the deadline, lower resolution/duration or switch tier rather than perpetually raising the cap.

Example fix

# before
provider._poll_deadline_s = 120   # 2 min; long generations time out

# after
provider._poll_deadline_s = 900   # 15 min, matches provider's long-queue worst case
Defensive patterns

Strategy: retry

Validate before calling

def deadline_fits_request(poll_deadline_s: int, duration_s: int, tier_queue_s: int) -> bool:
    # heuristic: queue + generation should fit inside the deadline
    return poll_deadline_s > tier_queue_s + duration_s * 2

Try / catch

try:
    video = provider.generate_and_poll(**kwargs)
except TimeoutError as exc:
    # outcome unknown — the job may still complete and bill
    log.warning("poll deadline hit: %s; checking provider state", exc)
    raise PendingJobUnknown(str(exc)) from exc

Prevention

When it happens

Trigger: A video generation queued long on the provider (busy queue, long duration/high resolution request) so it stays 'queued'/'in_progress' past the deadline; provider API degradation where status updates stall; deadline configured too low for the requested output.

Common situations: Default poll deadline tuned for short clips but the user requested a long high-res generation; provider incident with stuck jobs; slow-tier/free-tier queues with multi-minute waits.

Related errors


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/2408c58e7bcb298d. Report an issue: GitHub.