unsupported_media_type: video_url served as text/html or an image
Sume video trim and filter HEAD the source and refuse a declared non-video content type. What is checked, why octet-stream passes, and a runnable check.

unsupported_media_type on POST /v1/video-trim or POST /v1/video-filter means the HEAD request to your video_url came back with a declared content type that is not video/*, for example text/html from an error page or image/jpeg from a still. The message reads "video_url is served as ...; Video trim 1.0 needs a video source", and the refusal happens at submit, before a job is paid for.
What does the preflight actually read?
Per the API source (apps/api/src/media-l2.ts), the order is: the URL must be on the Sume media host (unsupported_media_source), a HEAD request must not report a missing file (source_not_found), a declared content type must be a video (unsupported_media_type), then the size must fit (source_too_large). application/octet-stream, binary/octet-stream and an empty type are treated as undeclared and pass this step, since the worker probes the real file later.
The Video trim page lists the worker-side twin, unsupported_media_type: HEAD is not a video, so you may meet the code at either stage.
| Code | Cause | Fix |
|---|---|---|
| unsupported_media_source | URL is not on the Sume media host | Import it first with POST /v1/media-imports |
| source_not_found | Dead or foreign media.sume.com URL | Use the durable asset URL from your workspace |
| unsupported_media_type | Declared content type is not video/* | Pass the video artifact, not a page or a still |
| source_too_large | Content-Length over 300 MiB | Use a smaller rendition |
Why does it happen with an FFmpeg habit?
ffmpeg -i URL opens whatever the URL serves and probes the bytes. The hosted API refuses early on the declared type, because a wrong URL is far more often a landing page, an expired link, or an image than a damaged video. The usual cause is passing a page URL or a provider's temporary link instead of the mirrored media.sume.com artifact URL.
Take the video_url from a finished job's result, or from the asset you imported, and not from a browser address bar.
How do I test a URL before submitting?
For filters, the free POST /v1/video-filter/check runs the same Sume-host and HEAD preflight with no job. For everything else, the helper below mirrors the checks on a content type and length you read yourself, and it runs as written.
If the HEAD says video/mp4 and the job still fails, read the job error: the worker probes the real bytes, and a truncated or audio-only file fails there.
MAX_BYTES = 300 * 1024 * 1024 # 314,572,800
def preflight(content_type, content_length):
ct = (content_type or "").split(";")[0].strip().lower()
declared = ct and ct not in ("application/octet-stream", "binary/octet-stream")
if declared and not ct.startswith("video/"):
return "unsupported_media_type"
if content_length is not None and content_length > MAX_BYTES:
return "source_too_large"
return "ok"
print(MAX_BYTES)
print(preflight("video/mp4", 120_000_000)) # ok
print(preflight("video/mp4", 400_000_000)) # source_too_large
print(preflight("text/html; charset=utf-8", 5)) # unsupported_media_type
print(preflight("application/octet-stream", None)) # okWhat does a clean source URL look like?
A media.sume.com artifact or asset URL that serves a video type, either video/mp4 or another video/* value. Results from earlier Sume jobs already look like this, and so do files you bring in through POST /v1/media-imports. If a HEAD request in your terminal returns text/html, the link is a page or an error, and no amount of retrying will change the answer.
The same URL rules apply across the media calls, so a link that works for an inspect or a frames call works for trim and filter, and a link refused by one is refused by all. Fix the URL once, and the whole chain behaves.
Which codes should I handle together?
Handle the four source codes in one place in your client, because they share one cause: the URL does not point at a usable hosted video. A single handler can map unsupported_media_source and source_not_found to an import step, unsupported_media_type to a wrong-link message, and source_too_large to a smaller rendition.
None of them is retryable as is. Retrying the same URL returns the same answer, so the handler should change something, the link or the file, before it tries again.
Sources
Related posts
More in Developers
- Valibot safeParse on a Sume job status: keep polling on bad data
Valibot's safeParse returns a result instead of throwing, so a malformed Sume status body can be logged and retried. A short schema and runnable code.
- Validate video duration and resolution in Python before you submit
Fetch GET /v1/videos/models and check duration, resolution and aspect_ratio per model in about 25 lines of Python, before a Sume video job fails.
- Veo 2.0 and Veo 3.0 shut down June 30: what model id to call now
Google retired veo-2.0 and veo-3.0 ids on 2026-06-30. See which Veo and Omni ids the Gemini docs list now, and how to move the call to Sume.
- Edited a script? Reuse unchanged TTS takes with verify-spine
After a script edit, Sume's read-only verify-spine route checks which TTS takes still cover the accepted sentences, so you regenerate only what changed.
Written by Sume