Avatar photo 400 invalid_image: under 64 px or a 6:1 aspect ratio
Sume rejects an avatar photo that is not decodable, is under 64 px on a side, or is more than 6:1. The 400 invalid_image error reports width, height and reason.
400 invalid_image on a Sume avatar photo means the file arrived with an accepted content type, but the pixels fail one of three checks: the header cannot be decoded as the type it claims, either side is under 64 px, or the long side is more than six times the short side. details.reason tells you which one: image_not_decodable, image_too_small or unsupported_aspect_ratio, with width and height on the last two.
Like every avatar photo check, it runs before any job is created, so nothing is queued and you can fix the picture and resubmit. The request shape is on Create new avatar, read 2026-10-05.
The three reasons
Sume reads the image header itself, for PNG, JPEG, WebP and GIF, instead of trusting the Content-Type. That is how a JPEG served with an image/png label ends up as image_not_decodable: the PNG signature is missing.
- Check the file, not the label: open it and read the real dimensions.
- Crop banners and strips to a portrait or square frame before upload.
- Keep both sides at 64 px or more, and well above that for a usable face.
- Log details.width and details.height from the error and compare with your stored values.
| details.reason | Rule | Example that fails | Example that passes |
|---|---|---|---|
| image_not_decodable | Header must parse as the declared type | 4 random bytes labeled image/png | A real PNG, JPEG, WebP or GIF |
| image_too_small | Both sides at least 64 px | 63 x 400 | 64 x 64 |
| unsupported_aspect_ratio | Long side / short side at most 6 | 1300 x 200 (6.5) | 1200 x 200 (exactly 6) |
Check the numbers locally
The three rules fit in a few lines, so run them on your own files before you call the API. This sketch checks the numbers only; read the width and height with your image library of choice.
def check(width, height):
if width < 64 or height < 64:
return "image_too_small"
if max(width, height) / min(width, height) > 6:
return "unsupported_aspect_ratio"
return "ok"
for size in [(63, 400), (64, 64), (1200, 200), (1300, 200), (1024, 1536)]:
print(size, check(*size))What to send instead
The error also sets details.suggested_input to clear_front_facing_image. That is the whole hint Sume gives, and it is a fair summary of what to send: a normal portrait crop, not a banner, not a thumbnail. A 9:16 or 3:4 phone photo is nowhere near the 6:1 limit, and neither is a square profile picture.
Sume does not promise that a photo which passes will look good as an avatar. The preflight is a gate for broken inputs. To judge the look of the person, create the avatar and review the first-frame stills in an avatar video preview before a full render.
Which common images trip the ratio rule
A 6:1 limit is generous. A 16:9 frame is 1.78:1, a 9:16 phone shot is 1.78:1, and even a 21:9 cinema crop is 2.33:1. The rule exists to stop banners, strips and sprite sheets, not to police normal photography. If you hit it, the usual cause is a cropped logo bar or a contact-sheet image that was uploaded as the headshot.
The 64 px floor is the same in both directions, so a tall thin 63 x 400 image fails on the small side even though its ratio of 6.35 would also fail. The API reports the first rule it trips, so fix both before you retry.
Log what Sume saw
Because the numbers come back in details.width and details.height, a client can log exactly what Sume saw. That is useful when your CDN resizes on the fly: the dimensions you stored may not be the dimensions served to Sume's fetch. Compare them before you blame the file.
Where undecodable files come from
Two quick causes explain most image_not_decodable errors. A CDN that converts formats on the fly can send AVIF bytes under a PNG label, and a download that was cut short has a header but no body. Re-download the file, open it, and host it again. For the type errors that come one step earlier, see unsupported_image_type.
Sources
Related posts
More in Developers
- Avatar photo URL redirects: five hops, then image_not_fetchable
Sume follows up to five redirects for an avatar photo and re-checks every hop. A redirect to http or a private address returns 400 image_not_fetchable.
- Avatar photo URL rejected: http, a port, a password or localhost
Sume refuses an avatar image_url that is not public HTTPS: no http, port, user:password or localhost. It fails on validation, with no fetch. Fix and retry.
- Avatar submit response: what next_action tells your client to do
next_action has three values on avatar submits: poll_status while queued or processing, fetch_result once completed, inspect_events for failed or canceled jobs.
- Avatar sync wait: timed_out vs capacity_exhausted, and what to do
A sync avatar submit can return 2xx with sync.timed_out or sync.capacity_exhausted true. Both mean keep the job: poll status_url, never submit a new paid job.
Written by Sume