{"record":{"id":"2408c58e7bcb298d","repo":"NousResearch/hermes-agent","slug":"video-job-getattr-video-id-did-not-reac","errorCode":null,"errorMessage":"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})","messagePattern":"video job (.+?) did not reach a terminal status within (.+?)s \\(last status=(.+?)\\)","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"warning","filePath":"agent/video_gen_provider.py","lineNumber":440,"sourceCode":"    def is_available(self) -> bool:\n        return bool(self._api_key())\n\n    def _create_and_poll(self, client: Any, call_kwargs: Dict[str, Any]) -> Any:\n        \"\"\"Create the video job and poll to completion with a hard deadline.\n\n        Replaces ``client.videos.create_and_poll`` (unbounded 1/s loop) with a\n        coarse interval and a wall-clock cap. Returns the terminal video object\n        (any status); raises :class:`TimeoutError` if the deadline passes\n        first.\n        \"\"\"\n        import time\n\n        video = client.videos.create(**call_kwargs)\n        terminal = {\"completed\", \"succeeded\", \"failed\", \"error\", \"cancelled\", \"canceled\"}\n        deadline = time.monotonic() + self._poll_deadline_s\n        while getattr(video, \"status\", None) not in terminal:\n            if time.monotonic() >= deadline:\n                raise TimeoutError(\n                    f\"video job {getattr(video, 'id', '?')} did not reach a terminal \"\n                    f\"status within {int(self._poll_deadline_s)}s \"\n                    f\"(last status={getattr(video, 'status', None)!r})\"\n                )\n            time.sleep(self._poll_interval_s)\n            video = client.videos.retrieve(video.id)\n        return video\n\n    def _base_url(self) -> str:\n        import os\n\n        override = os.environ.get(f\"{self.name.upper()}_BASE_URL\", \"\").strip()\n        return override or self._default_base_url\n\n    def generate(\n        self,\n        prompt: str,\n        *,","sourceCodeStart":422,"sourceCodeEnd":458,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/video_gen_provider.py#L422-L458","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Increase the provider's poll deadline (_poll_deadline_s) to cover the worst-case queue time for the requested duration/resolution.","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.","Treat this timeout as 'unknown outcome': the job may still complete and bill; verify on the provider side before regenerating.","If jobs are systematically slower than the deadline, lower resolution/duration or switch tier rather than perpetually raising the cap."],"exampleFix":"# before\nprovider._poll_deadline_s = 120   # 2 min; long generations time out\n\n# after\nprovider._poll_deadline_s = 900   # 15 min, matches provider's long-queue worst case","handlingStrategy":"retry","validationCode":"def deadline_fits_request(poll_deadline_s: int, duration_s: int, tier_queue_s: int) -> bool:\n    # heuristic: queue + generation should fit inside the deadline\n    return poll_deadline_s > tier_queue_s + duration_s * 2","typeGuard":null,"tryCatchPattern":"try:\n    video = provider.generate_and_poll(**kwargs)\nexcept TimeoutError as exc:\n    # outcome unknown — the job may still complete and bill\n    log.warning(\"poll deadline hit: %s; checking provider state\", exc)\n    raise PendingJobUnknown(str(exc)) from exc","preventionTips":["Set _poll_deadline_s above the provider's worst-case queue+generation time for your tier.","On timeout, check the job id on the provider before regenerating (double billing).","Keep the coarse poll interval — the wall-clock cap, not the interval, is the safety control."],"tags":["video","polling","timeout","provider"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}