Reference video job failed on download: check input URLs first
A Sume reference-to-video job fails when an input URL is not public. Python that HEAD-checks references before POST /v1/videos, and how to read the error.

If a Sume reference-to-video job fails with a message about not downloading an input media URL, the file was not publicly reachable when Sume fetched it. Check every URL with a HEAD request before you submit: it must be public HTTPS, return 200, and look like the media type you declared. A failed job with this error is terminal, so you fix the URL and submit a new job.
What the failure looks like
Sume does not return the raw provider wrapper. The poll response for a failed job carries a public remap of the error, and for an unreachable image or video reference it reads: Could not download an input media URL (image_url). Verify the URL is publicly reachable, then retry. The URL itself stays private in the response.
On a terminal failure the content route answers 409 job_failed, with retryable false, so do not wait on content. Read the error field of GET /v1/videos/{id} instead.
Think about who fetches the file. It is Sume's worker, not your laptop, so a URL that opens in your browser because you are logged in, or because it is on your VPN, can still fail. The only reliable test is an anonymous request from outside your network, which the HEAD check below approximates.
Which fields take URLs
The mode is inferred from which field you send. Frame images pin a first or last frame for image-to-video. Input references condition a reference-to-video clip. Both carry URLs that Sume fetches from its side, so a link behind a login, a signed URL that expires in a minute, or a private bucket fails the same way.
| Field | Mode | Accepted types | Note |
|---|---|---|---|
| frame_images | Image-to-video | image_url with frame_type | first_frame or last_frame |
| input_references | Reference-to-video | image_url, video_url, audio_url | Per model, see supported_input_references |
| callback_url | Webhook | HTTPS URL | Must be public, not a URL you fetch |
| video_url on Omni | Edit | Video Router field only | Source clip, not in the OpenRouter-shaped body |
A preflight check
The script sends a HEAD request to each reference, follows redirects, and fails fast on anything that is not a 200 with an image or video content type. HEAD is not perfect, since some servers reject it, so fall back to a ranged GET for those.
import urllib.request, urllib.error
def check(url: str, kinds=("image/", "video/")) -> str:
if not url.startswith("https://"):
return "not https"
req = urllib.request.Request(url, method="HEAD", headers={"User-Agent": "preflight"})
try:
with urllib.request.urlopen(req, timeout=10) as r:
ctype = r.headers.get("content-type", "")
if r.status != 200:
return f"status {r.status}"
if not ctype.startswith(kinds):
return f"content-type {ctype!r}"
return "ok"
except urllib.error.HTTPError as e:
return f"http {e.code}"
except Exception as e:
return f"error {type(e).__name__}"
refs = ["https://www.sume.com/robots.txt", "http://example.com/a.png", "https://example.invalid/x.jpg"]
for u in refs:
print(u, "->", check(u))
Why this saves real money
A job that fails before generation starts is refunded, since Sume releases the reservation on failures where it applies. You still lose the wait, and in a batch you lose the whole wave slot, because a bad URL sits in your queue for minutes before it fails. A 10-second check up front costs nothing.
Treat the check as one step in a longer preflight. Compare the model's supported_input_references with the types you are sending, because a reference type that one model accepts is a 400 unsupported_capability on another. Omni, for instance, accepts image and video references but not audio ones.
If you run many jobs against the same reference set, check each URL once and cache the answer for the run. A character sheet or a product photo reused across 50 clips needs one check, not 50, and one fix when it breaks.
Hosting tips
- Use durable public HTTPS URLs, for example your own storage or CDN, not a short-lived signed link.
- Keep inputs for as long as the job might run, which can be many minutes for a long clip.
- If you retry the same intent after fixing the URL, change the Idempotency-Key, since the body changed.
Sources
Related posts
More in Developers
- CI test: assert Seedance 2.5 and Wan 3.0 still list 30 seconds
Fail a build when your 30-second video code outlives the catalog. A short Python test reads supported_durations from GET /v1/videos/models and checks 30.
- Retry, fix or stop: classify every Sume /v1/videos error in code
One Python function that maps each documented /v1/videos error code (400, 402, 404, 409, 429, 502) to retry, fix or stop, plus the two 409s that look alike.
- Claude Agent SDK init message: check the Sume server status first
Read the init message's mcp_servers statuses before a paid Sume call. failed and needs-auth mean the tools are not usable. pending is not a failure on its own.
- Claude Agent SDK allowedTools mcp__sume__* also allows paid Sume tools
A wildcard in allowedTools approves every tool the Sume server exposes. With an API key that includes paid ones. Name the read tools instead.
Written by Sume