{"record":{"id":"5d4ee0ce135ac15e","repo":"NousResearch/hermes-agent","slug":"video-at-url-was-empty-0-bytes","errorCode":null,"errorMessage":"Video at {url} was empty (0 bytes).","messagePattern":"Video at (.+?) was empty \\(0 bytes\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"agent/video_gen_provider.py","lineNumber":320,"sourceCode":"                continue\n            bytes_written += len(chunk)\n            if bytes_written > max_bytes:\n                fh.close()\n                try:\n                    path.unlink()\n                except OSError:\n                    pass\n                raise ValueError(\n                    f\"Video at {url} exceeds {max_bytes // (1024 * 1024)}MB cap; refusing to cache.\"\n                )\n            fh.write(chunk)\n\n    if bytes_written == 0:\n        try:\n            path.unlink()\n        except OSError:\n            pass\n        raise ValueError(f\"Video at {url} was empty (0 bytes).\")\n\n    return path\n\n\ndef success_response(\n    *,\n    video: str,\n    model: str,\n    prompt: str,\n    modality: str = \"text\",\n    aspect_ratio: str = \"\",\n    duration: int = 0,\n    provider: str,\n    extra: Optional[Dict[str, Any]] = None,\n) -> Dict[str, Any]:\n    \"\"\"Build a uniform success response dict.\n\n    ``video`` may be an HTTP URL or an absolute filesystem path.","sourceCodeStart":302,"sourceCodeEnd":338,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/video_gen_provider.py#L302-L338","documentation":"ValueError from agent/video_gen_provider.py:320: the streamed download of the generated video completed but wrote zero bytes, so the (empty) partial file is unlinked and the error raised. It distinguishes 'server returned nothing at this URL' from other failures.","triggerScenarios":"The video URL responds with an empty body — provider marked the job complete but the asset URL is not yet populated, a signed URL expired returning an empty 200, or a proxy/CDN hiccup delivered an empty stream.","commonSituations":"Racing a provider whose status turns 'completed' before the file is actually served; expired pre-signed S3/R2 URLs on retry; provider-side incident serving empty responses.","solutions":["Verify the URL outside the app: curl -sL <url> -o /tmp/v && ls -l /tmp/v — 0 bytes confirms the provider side.","Retry the download after a short delay (asset propagation lag right after job completion is the most common cause).","Regenerate or re-request the asset URL if the signed link expired.","If persistent, check the provider's status page / job logs for a partial-failure state."],"exampleFix":"# before (single attempt, empty body aborts the whole generation)\npath = _cache_video(url, max_bytes)\n\n# after (one bounded retry for propagation lag)\nfor attempt in range(2):\n    try:\n        path = _cache_video(url, max_bytes)\n        break\n    except ValueError as exc:\n        if \"was empty\" not in str(exc) or attempt == 1:\n            raise\n        time.sleep(5)","handlingStrategy":"retry","validationCode":"import urllib.request\n\ndef url_has_body(url: str) -> bool:\n    req = urllib.request.Request(url, method=\"HEAD\")\n    with urllib.request.urlopen(req, timeout=10) as r:\n        length = r.headers.get(\"Content-Length\")\n        return length is None or int(length) > 0","typeGuard":null,"tryCatchPattern":"for attempt in range(2):\n    try:\n        path = cache_video(url, max_bytes)\n        return path\n    except ValueError as exc:\n        if \"was empty\" not in str(exc) or attempt == 1:\n            raise\n        time.sleep(5)  # asset propagation lag; retry once","preventionTips":["Expect empty bodies right after job completion; download after a short delay or with one retry.","Use freshly issued asset URLs — expired signed URLs often return empty 200s.","Verify suspicious URLs with curl -sL <url> -o test.bin && ls -l test.bin."],"tags":["video","download","empty-response","provider"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}