{"record":{"id":"600b688698393315","repo":"invoke-ai/InvokeAI","slug":"failed-to-read-image-600b68","errorCode":null,"errorMessage":"Failed to read image","messagePattern":"Failed to read image","errorType":"http","errorClass":"HTTPException","httpStatus":415,"severity":"warning","filePath":"invokeai/app/api/routers/workflows.py","lineNumber":295,"sourceCode":"    try:\n        existing = await asyncio.to_thread(ApiDependencies.invoker.services.workflow_records.get, workflow_id)\n    except WorkflowNotFoundError:\n        raise HTTPException(status_code=404, detail=\"Workflow not found\")\n\n    config = ApiDependencies.invoker.services.configuration\n    if config.multiuser and not current_user.is_admin and existing.user_id != current_user.user_id:\n        raise HTTPException(status_code=403, detail=\"Not authorized to update this workflow\")\n\n    if not image.content_type or not image.content_type.startswith(\"image\"):\n        raise HTTPException(status_code=415, detail=\"Not an image\")\n\n    contents = await image.read()\n    try:\n        pil_image = await asyncio.to_thread(Image.open, io.BytesIO(contents))\n\n    except Exception:\n        ApiDependencies.invoker.services.logger.error(traceback.format_exc())\n        raise HTTPException(status_code=415, detail=\"Failed to read image\")\n\n    try:\n        await asyncio.to_thread(ApiDependencies.invoker.services.workflow_thumbnails.save, workflow_id, pil_image)\n    except Exception as e:\n        raise HTTPException(status_code=500, detail=str(e))\n\n\n@workflows_router.delete(\n    \"/i/{workflow_id}/thumbnail\",\n    operation_id=\"delete_workflow_thumbnail\",\n    responses={\n        200: {\"model\": WorkflowRecordDTO},\n    },\n)\ndef delete_workflow_thumbnail(\n    current_user: CurrentUserOrDefault,\n    workflow_id: str = Path(description=\"The workflow to update\"),\n):","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/invoke-ai/InvokeAI/blob/0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06/invokeai/app/api/routers/workflows.py#L277-L313","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nawait fetch(url, {method:'POST', body: JSON.stringify({data: base64})}); // server gets garbage\n// after\nconst blob = await file.arrayBuffer();\nawait fetch(url, {method:'POST', body: new Blob([blob], {type: file.type})});","handlingStrategy":"try-catch","validationCode":"// decode client-side first to guarantee Pillow can read it\nconst img = await createImageBitmap(file); // throws if not a decodable image","typeGuard":"function isDecodableImage(f) { try { return !!f.size && f.type.startsWith('image/'); } catch { return false; } }","tryCatchPattern":"try { await uploadThumbnail(id, file); } catch (e) { if (e?.status === 415) notify('Image could not be decoded; re-export as PNG'); else throw e; }","preventionTips":["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"],"tags":["http-415","image-parsing","pillow","upload"],"backgroundTag":"invalid-image-format","analyzedSha":"0b6a024f2ff6a86bfb953dcdb9cc504ef7397a06","analyzedAt":"2026-08-29T04:46:49.967Z","schemaVersion":2},"datasetVersion":"2026-08-29T07:17:48.351Z"}