Handle avatar photo errors in Python: read details.suggested_input

A stdlib Python handler for Sume avatar photo errors: branch on error code, print the suggested input, and never echo a private URL. Refuses an empty API key.

5 min readSume
All posts

To handle Sume avatar photo errors in Python, branch on error.code and read error.details. The photo preflight returns four codes: image_not_fetchable and unsupported_image_type with 400, invalid_image with 400 and image_too_large with 413. Every one carries details.field of file.url, details.url_host and details.suggested_input, and none creates a job.

The shared error envelope, including request_id, is described in the error reference, and the photo rules are on Create new avatar, both read 2026-10-05.

Four codes, four actions

The table gives the four photo codes and the field to act on. Use it to decide what your user sees: most of these are fixable by the person who owns the photo.

Notice what the table leaves out. A 5xx or a network failure is not a photo problem, and the right response is a bounded retry with the same Idempotency-Key, not advice to the user. Only the four photo codes should ever reach the person who supplied the picture. Everything else belongs in your logs and your on-call channel, with the request id attached.

Also separate the photo codes from the request-shape codes. A removed field or a missing handle returns invalid_request with details about fields, which is a bug in your code, not in the photo. Showing a user a message about their photo when your client sent the wrong field name is a confusing experience and wastes support time.

Photo preflight codes and fixes, read 2026-10-05
error.codeHTTPRead in detailsWhat to tell the user
image_not_fetchable400status, reasonUse a public HTTPS link that opens without login
unsupported_image_type400content_typeSend a PNG, JPEG, WebP or GIF
invalid_image400reason, width, heightUse a larger, less stretched portrait
image_too_large413max_bytes, size_bytes or width, heightDownscale the photo

The handler

This standard-library script posts one avatar and maps each code to advice. It exits at once when SUME_API_KEY is empty. It prints url_host instead of the URL, which keeps signed links out of logs.

import json, os, sys, urllib.request, urllib.error

key = os.environ.get("SUME_API_KEY", "")
if not key:
    sys.exit("Set SUME_API_KEY")
ADVICE = {
    "image_not_fetchable": "Use a public HTTPS image link.",
    "unsupported_image_type": "Send PNG, JPEG, WebP or GIF.",
    "invalid_image": "Use a bigger portrait, under 6:1.",
    "image_too_large": "Downscale below 16384 px.",
}
body = json.dumps({"avatar_handle": "presenter_01", "input": {
    "type": "photo", "image_url": sys.argv[1]}}).encode()
req = urllib.request.Request("https://api.sume.com/v1/avatar-1.0/generate",
    data=body, method="POST", headers={"Authorization": "Bearer " + key,
    "Content-Type": "application/json", "Idempotency-Key": "photo-handler-01"})
try:
    print(urllib.request.urlopen(req).read().decode())
except urllib.error.HTTPError as e:
    err = json.loads(e.read().decode()).get("error", {})
    d = err.get("details") or {}
    print(e.code, err.get("code"), d.get("url_host"), d.get("suggested_input"))
    print(ADVICE.get(err.get("code"), err.get("message")))

Run it

Run it as python3 handler.py https://assets.example.com/headshot.jpg. A rejected photo prints one line of facts and one line of advice. An accepted photo creates a $0.95 avatar job, so use a photo you intend to keep.

The output has the same shape for every code: status, code, host and hint on the first line, then one sentence of advice. That makes it easy to feed into a form message. If the code is not one of the four, the script prints the API's own message, which keeps unknown failures visible instead of hiding them behind generic text.

To adapt it, replace the print calls with your logging and UI. Keep the empty-key guard, which fails fast with a clear message instead of sending an unauthenticated request and getting an authentication error back that obscures the real problem.

Three habits for the handler

Three habits keep the handler honest. First, branch on the code and not on the message, since messages are for people. Second, treat details as optional: only the fields shown in the table are promised for each code, and a missing key should not crash your handler, which is why the script uses .get. Third, log request_id from the envelope so support can find the request.

For the pixel limits behind invalid_image and image_too_large, see the 64 px post and the 413 post. For fetch failures, see the 15 second post.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume