invoke-ai/InvokeAI · error · HTTPException

Not an image

Error message

Not an image

What it means

update_style_preset checks the uploaded file's content_type: if it is missing or does not start with 'image', the endpoint returns HTTP 415 'Not an image'. This guards the PIL decode path, which only accepts image data. It is an Unsupported Media Type rejection based on the multipart part's declared MIME type.

Source

Thrown at invokeai/app/api/routers/style_presets.py:127

    try:
        parsed_data = json.loads(data)
        validated_data = StylePresetFormData(**parsed_data)

        name = validated_data.name
        type = validated_data.type
        positive_prompt = validated_data.positive_prompt
        negative_prompt = validated_data.negative_prompt
        is_public = validated_data.is_public

    except (json.JSONDecodeError, pydantic.ValidationError):
        raise HTTPException(status_code=400, detail="Invalid preset data")

    record = await asyncio.to_thread(_load_record_or_404, style_preset_id)
    _assert_preset_write(record, current_user)

    if image is not None:
        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.style_preset_image_files.save, style_preset_id, pil_image
            )
        except ValueError as e:
            raise HTTPException(status_code=409, detail=str(e))
    else:
        try:
            await asyncio.to_thread(ApiDependencies.invoker.services.style_preset_image_files.delete, style_preset_id)

View on GitHub (pinned to 0b6a024f2f)

Solutions

  1. Send the file so its part has an image/* Content-Type (e.g. image/png, image/jpeg).
  2. Set Blob.type / File MIME type in the browser client before appending to FormData.
  3. Verify the server actually received the file: if the client intends no image, omit the image part entirely (default None) rather than sending an empty non-typed part.
  4. Prefer sending a real image file; if you must send arbitrary bytes, convert to PNG client-side first.

Example fix

// before
const blob = new Blob([bytes]); // no type -> content_type null
formData.append("image", blob);

// after
const blob = new Blob([bytes], { type: "image/png" });
formData.append("image", blob, "preset.png");
Defensive patterns

Strategy: validation

Validate before calling

function canUploadImage(file) {
  return !!file && typeof file.type === "string" && file.type.startsWith("image/") && file.size > 0;
}

if (image && !canUploadImage(image)) throw new Error(`refusing upload: content-type=${image?.type}`);

Type guard

function isImageFile(f: unknown): f is File {
  return f instanceof File && f.type.startsWith("image/") && f.size > 0;
}

Try / catch

try {
  await api.updateStylePreset({ stylePresetId, data, image });
} catch (e) {
  if (e.status === 415 && e.detail === "Not an image") {
    console.error("Image part content-type is not image/*:", image?.type);
  } else throw e;
}

Prevention

When it happens

Trigger: PATCH /style_presets/i/{id} with an optional image part whose Content-Type is absent, application/json, text/plain, application/pdf, application/octet-stream, or any non-image/* type.

Common situations: HTTP clients that don't set MIME type on blobs (e.g. fetch with a raw Blob lacking type); uploading placeholder .txt or .pdf files; proxies stripping Content-Type on multipart parts; test harnesses sending the image as a plain string field.

Related errors


AI-assisted analysis of invoke-ai/InvokeAI@0b6a024f2f (2026-08-29). Data as JSON: /api/errors/635554b5a1bdb824. Report an issue: GitHub.