Avatar photo 400 unsupported_image_type: PNG, JPEG, WebP, GIF only

Sume's avatar photo preflight accepts four image content types. An HTML page, AVIF or HEIC answer returns 400 unsupported_image_type and no job is created.

4 min readSume
All posts

If creating a photo avatar on Sume returns 400 unsupported_image_type, the URL you sent as input.image_url answered with a Content-Type that is not image/png, image/jpeg, image/webp or image/gif. Sume checks the header of the response, not the file extension. A login page, an expired signed link that now returns text/html, or an iPhone HEIC file served as image/heic all fail the same way.

The check is a preflight. Sume fetches the photo before it creates the avatar job, so a rejected photo leaves no job behind. The error carries details.content_type with what the server actually sent, details.url_host with only the host, and details.suggested_input set to clear_front_facing_image. The rest of the contract is on Create new avatar and Media inputs (both read 2026-10-05).

What the preflight compares

The preflight sends a GET with Accept: image/png,image/jpeg,image/webp,image/gif. Then it normalizes the response Content-Type and compares it with the list. Parameters such as ; charset=binary are stripped before the comparison.

Content types and the preflight result, read 2026-10-05
Response Content-TypeResultWhy
image/pngContinues to the size checksOn the accepted list
image/jpeg or image/jpgContinues to the size checksOn the accepted list
image/webpContinues to the size checksOn the accepted list
image/gifContinues to the size checksOn the accepted list
text/html400 unsupported_image_typeNot an image; often a page or an error body
image/avif, image/heic, image/tiff, image/svg+xml400 unsupported_image_typeImage types, but not on the list of four
Missing header400 unsupported_image_type, content_type nullNothing to match

Check the header first

Run the same request your client will make and read the header before you submit anything paid.

curl -sS -L -o /dev/null -w '%{http_code} %{content_type}\n' \
  -H 'Accept: image/png,image/jpeg,image/webp,image/gif' \
  'https://assets.example.com/headshot.jpg'
# want: 200 image/jpeg (or png, webp, gif)
# 200 text/html means the link is a page, not the file

How to fix it

Fix the source, not the request. Export the picture as JPEG or PNG and host that file. If the host guesses types from the extension, make sure the file name matches the bytes. If your photos live behind a signed link, generate the link right before you call Sume, because an expired link usually answers with an HTML or JSON error page.

A photo that passes the type check can still fail on its pixels. That is a different code, invalid_image, and it is covered in the 64-pixel and aspect-ratio post.

Why a rejected photo costs nothing

A job-based API that creates a billable avatar has a reason to fail early. Avatar creation is a fixed-price step of $0.95 per avatar in Sume's pricing package, and the usage reservation is made after the photo preflight in the creation handler. So an unsupported_image_type response is free: no job exists, and no reservation was made for it. That ordering is visible in the source and is why the error is safe to retry in a loop while you fix a hosting problem.

Keep one thing in mind when you build a retry loop. A 400 from the preflight will not change by waiting. Only the host's answer changes, so retrying without changing the URL or the file at that URL just repeats the same rejection.

Where to put the check in your own flow

Put a small guard in front of your own upload flow. Fetch the URL with a HEAD or GET once, read the content type, and only then call Sume. Teams that accept photos from users can run the same check at upload time and show the person a clear message instead of a raw API error.

If your pipeline converts files, convert to JPEG or PNG at the end of the pipeline, not in the middle. Re-encoding a WebP to AVIF for size, then sending the AVIF, is the most common way to land in this error by accident.

What the error tells you, and what it hides

The error body never repeats a query string. A test in the API suite sends photo.txt?token=private and asserts that the token is absent from the response, so you can paste the error into a ticket without leaking a signed link. The shared error envelope, including request_id, is on Errors and rate limits.

Sume's canonical create route is POST /v1/avatar-1.0/generate with input: {"type": "photo", "image_url": ...}. Internally the URL is validated as file.url, which is why details.field reads file.url even when you sent input.image_url.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume