NousResearch/hermes-agent · error · ValueError

Image at {url} returned 0 bytes; refusing to cache.

Error message

Image at {url} returned 0 bytes; refusing to cache.

What it means

Raised by the image-generation provider's cache writer after streaming an image from a remote URL produced zero bytes. The empty file is unlinked and the download is aborted so a 0-byte artifact never poisons the cache. It signals that the provider URL responded but delivered no body (or the read loop received no chunks).

Source

Thrown at agent/image_gen_provider.py:342

                continue
            bytes_written += len(chunk)
            if bytes_written > max_bytes:
                fh.close()
                try:
                    path.unlink()
                except OSError:
                    pass
                raise ValueError(
                    f"Image at {url} exceeds {max_bytes // (1024 * 1024)}MB cap; refusing to cache."
                )
            fh.write(chunk)

    if bytes_written == 0:
        try:
            path.unlink()
        except OSError:
            pass
        raise ValueError(f"Image at {url} returned 0 bytes; refusing to cache.")

    return path


def success_response(
    *,
    image: str,
    model: str,
    prompt: str,
    aspect_ratio: str,
    provider: str,
    modality: str = "text",
    extra: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """Build a uniform success response dict.

    ``image`` may be an HTTP URL or an absolute filesystem path (for b64
    providers like OpenAI). ``modality`` is ``"text"`` (text-to-image) or

View on GitHub (pinned to c896c09c42)

Solutions

  1. Retry the generation once — transient empty-body responses from image providers usually succeed on a second call.
  2. Verify the URL is fresh: if the provider hands back short-lived signed URLs, re-request the image metadata to obtain a new URL before downloading.
  3. Check with curl -I <url> that the URL actually returns Content-Length > 0 and a 200; if it 403s or redirects, fix the provider credentials/redirect handling.
  4. Inspect provider API docs/changelog for response-format changes (e.g. b64_json vs url) and update the response parsing.

Example fix

// before
const result = await provider.generate({ prompt });
await cacheImage(result.url); // throws ValueError: 0 bytes

// after
const result = await provider.generate({ prompt });
const head = await fetch(result.url, { method: 'HEAD' });
if (!head.ok || Number(head.headers.get('content-length') ?? 0) === 0) {
  throw new Error(`provider returned empty image at ${result.url}; retry generation`);
}
await cacheImage(result.url);
Defensive patterns

Strategy: retry

Validate before calling

import urllib.request

def image_url_has_content(url: str) -> bool:
    req = urllib.request.Request(url, method="HEAD")
    with urllib.request.urlopen(req, timeout=10) as resp:
        return resp.status == 200 and int(resp.headers.get("Content-Length") or 0) > 0

Try / catch

try:
    path = cache_image(url)
except ValueError as e:
    if "0 bytes" in str(e):
        path = cache_image(refresh_image_url(url))  # one retry with a fresh URL
    else:
        raise

Prevention

When it happens

Trigger: Calling the image cache/download path (e.g. image_gen tool with a provider that returns a URL) where the HTTP response body is empty: a signed URL that expired and returns 200 with no content, a redirect handled without follow, or a provider that streams zero chunks before closing.

Common situations: Expired or mis-scoped presigned S3/GCS URLs; provider API changes returning metadata instead of bytes; proxies or gateways stripping the body; transient provider outages that return 200-empty.

Related errors


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