invoke-ai/InvokeAI · warning · HTTPException
Failed to read image
Error message
Failed to read image
What it means
HTTP 415 raised when PIL (Pillow) fails to open the uploaded bytes as an image. The endpoint logs the full traceback server-side and returns 'Failed to read image', so the file was claimed to be an image but its content is undecodable.
Source
Thrown at invokeai/app/api/routers/workflows.py:295
try:
existing = await asyncio.to_thread(ApiDependencies.invoker.services.workflow_records.get, workflow_id)
except WorkflowNotFoundError:
raise HTTPException(status_code=404, detail="Workflow not found")
config = ApiDependencies.invoker.services.configuration
if config.multiuser and not current_user.is_admin and existing.user_id != current_user.user_id:
raise HTTPException(status_code=403, detail="Not authorized to update this workflow")
if not image.content_type or not image.content_type.startswith("image"):
raise HTTPException(status_code=415, detail="Not an image")
contents = await image.read()
try:
pil_image = await asyncio.to_thread(Image.open, io.BytesIO(contents))
except Exception:
ApiDependencies.invoker.services.logger.error(traceback.format_exc())
raise HTTPException(status_code=415, detail="Failed to read image")
try:
await asyncio.to_thread(ApiDependencies.invoker.services.workflow_thumbnails.save, workflow_id, pil_image)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@workflows_router.delete(
"/i/{workflow_id}/thumbnail",
operation_id="delete_workflow_thumbnail",
responses={
200: {"model": WorkflowRecordDTO},
},
)
def delete_workflow_thumbnail(
current_user: CurrentUserOrDefault,
workflow_id: str = Path(description="The workflow to update"),
):View on GitHub (pinned to 0b6a024f2f)
Solutions
- Verify the file opens with any image viewer / PIL locally before uploading
- Convert to PNG or JPEG client-side and re-upload
- Ensure the upload reads raw binary (await file.arrayBuffer()/blob) without text encoding
- Check server logs for the PIL traceback to identify the exact decode failure
Example fix
// before
await fetch(url, {method:'POST', body: JSON.stringify({data: base64})}); // server gets garbage
// after
const blob = await file.arrayBuffer();
await fetch(url, {method:'POST', body: new Blob([blob], {type: file.type})}); Defensive patterns
Strategy: try-catch
Validate before calling
// decode client-side first to guarantee Pillow can read it const img = await createImageBitmap(file); // throws if not a decodable image
Type guard
function isDecodableImage(f) { try { return !!f.size && f.type.startsWith('image/'); } catch { return false; } } Try / catch
try { await uploadThumbnail(id, file); } catch (e) { if (e?.status === 415) notify('Image could not be decoded; re-export as PNG'); else throw e; } Prevention
- Re-encode images through canvas/ImageMagick before upload to strip corrupt metadata
- Reject empty files early (file.size === 0)
- Read files as binary (arrayBuffer/Blob), never via text paths
When it happens
Trigger: Uploading corrupted/truncated image data, an empty body, or a file with image/* Content-Type but non-image content (e.g. renamed JSON/HTML/SVG).
Common situations: Zero-byte files from aborted client reads; SVGs or WebP variants Pillow's build can't decode; upload middleware mangling binary data (UTF-8 re-encoding); truncated downloads before upload.
Related errors
- Failed to read image
- Not an image
- Not an image
- Video must use a browser-compatible H.264/AVC codec
- Video has no decodable frame
AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29).
Data as JSON: /api/errors/600b688698393315.
Report an issue: GitHub.