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.
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.
| error.code | HTTP | Read in details | What to tell the user |
|---|---|---|---|
| image_not_fetchable | 400 | status, reason | Use a public HTTPS link that opens without login |
| unsupported_image_type | 400 | content_type | Send a PNG, JPEG, WebP or GIF |
| invalid_image | 400 | reason, width, height | Use a larger, less stretched portrait |
| image_too_large | 413 | max_bytes, size_bytes or width, height | Downscale 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
- 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