Avatar photo image_not_fetchable: 404, timeouts and the 15 s limit

Sume gives an avatar photo host 15 seconds for fetch and body read. A non-2xx status or a timeout returns 400 image_not_fetchable. How to fix it.

4 min readSume
All posts

400 image_not_fetchable on an avatar photo means Sume's preflight could not get a usable response from your URL. Two things cause it most: your host answered with a status outside 200-299 (a 403, 404 or 500, reported in details.status), or nothing useful arrived within 15 seconds (then details.status is null). The preflight runs before a job exists, so a failure creates no avatar job and no usage reservation.

The error also carries details.url_host and details.suggested_input: public_image_url. The query string is never echoed. The behaviour is described on Create new avatar, and the generic code is in the error reference, both read 2026-10-05.

One clock for the whole preflight

In the source, one AbortController is armed for 15 seconds and its signal is passed to the fetch. The timer is cleared only after the response has been checked and the body has been read for dimensions. So the 15 seconds is a single budget for connecting, waiting for headers, following redirects and downloading the file, not a per-step limit.

Failure shapes of image_not_fetchable, read 2026-10-05
What happeneddetails.statusdetails.reasonTypical cause
Host answered 404404noneWrong path or deleted file
Host answered 403403noneHotlink protection or an expired signed link
Host answered 500500noneOrigin error on your side
No answer inside 15 seconds, or network errornullnoneSlow origin, firewall, DNS failure
Redirect chain too long or unsafenonetoo_many_redirects or unsafe_urlSee the redirect rules

Reproduce it with curl

Reproduce the exact fetch before you retry the API. The command below prints the status, the content type, the time taken, and the byte size, and it aborts at 15 seconds the way Sume does.

curl -sS -L --max-time 15 -o /dev/null \
  -H 'Accept: image/png,image/jpeg,image/webp,image/gif' \
  -w 'status=%{http_code} type=%{content_type} secs=%{time_total} bytes=%{size_download}\n' \
  'https://assets.example.com/headshot.jpg'
# status must be 2xx; secs must stay well under 15

The two causes that look like Sume's fault

A 403 is the surprise that bites teams most. Storage links with an expiry time are valid when you create them and invalid by the time a queue picks the work up, and some hosts return 403 to any client that lacks a browser-like referrer. Sume sends a plain GET with an image Accept header and nothing else, so a link that works only inside your app will fail here.

A timeout is the other common one. Photos stored in a cold region, behind a slow image resizer, or on a host that renders the image on first request can take longer than 15 seconds the first time and a second the next. Warm the URL once with the curl line above, then send it to Sume.

  • Expired signed links return 403 or 404 after the expiry time.
  • Hosts that block unknown clients return 403 to a plain GET.
  • A cold or resizing host can pass your test and miss the 15 second budget.
  • A response body that arrives slowly counts against the same clock as the headers.

Retry rules

Because a failed preflight costs nothing, retrying is safe. Use a short backoff and cap the attempts, and change something between attempts: warm the URL, extend the signed link lifetime, or move the file to a faster host. A retry that sends the same cold URL will get the same answer.

If the status is a 2xx and you still see image_not_fetchable, look at details.reason. A value of unsafe_url, too_many_redirects, redirect_without_location or invalid_url points to the URL or redirect rules, covered in the redirect post and the URL post.

What a good host looks like

Host the photo where a stranger's client can read it fast: a public object store or CDN with a stable path, a long-lived link, and an image content type. Then the other checks, such as content type and pixel rules, are the only ones left to pass.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume