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.

4 min readSume
All posts

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.

Source refusals for trim and filter, read 2026-10-03 from the Video trim and Video filter pages and API source
CodeCauseFix
unsupported_media_sourceURL is not on the Sume media hostImport it first with POST /v1/media-imports
source_not_foundDead or foreign media.sume.com URLUse the durable asset URL from your workspace
unsupported_media_typeDeclared content type is not video/*Pass the video artifact, not a page or a still
source_too_largeContent-Length over 300 MiBUse 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))  # ok

What 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

All Developers posts

Written by Sume