siyuan-note/siyuan · error
unsupported or invalid image
Error message
unsupported or invalid image: %s
What it means
This error is thrown by the image preparation routine in kernel/util/openai.go when the image bytes fail Go's image.DecodeConfig after the MIME type already passed the allowed gif/jpeg/png/webp check. It means the data is not decodable as a real image of the declared type — truncated download, corrupt file, or a wrong Content-Type/mime guess. The underlying Go decoder error text is appended to the message.
Solutions
- Verify the file opens in an image viewer or `file <path>` reports the expected image type
- Re-download / re-export the asset to repair truncation or corruption
- Check the actual MIME type (magic bytes) and ensure only gif/jpeg/png/webp images are passed in
- Convert the image to PNG or JPEG with an external tool before retrying
Example fix
// before: passing raw fetched bytes blindly
img, err := prepareImage(respBytes, "image/png", limits...)
// after: sniff actual format first
kind := mimetype.Detect(respBytes).String()
if kind != "image/png" && kind != "image/jpeg" && kind != "image/gif" && kind != "image/webp" {
return fmt.Errorf("not a supported image, got %s", kind)
}
img, err := prepareImage(respBytes, kind, limits...) Defensive patterns
Strategy: validation
Validate before calling
kind := mimetype.Detect(data).String()
if kind != "image/png" && kind != "image/jpeg" && kind != "image/gif" && kind != "image/webp" {
return fmt.Errorf("unsupported image type: %s", kind)
}
cfg, _, err := image.DecodeConfig(bytes.NewReader(data))
if err != nil {
return fmt.Errorf("image not decodable: %w", err)
} Try / catch
prepared, err := util.PrepareImageForAI(data, mime, limits...)
if err != nil {
if strings.HasPrefix(err.Error(), "unsupported or invalid image") {
// drop or re-encode the asset; skip AI vision for it
return nil
}
return err
} Prevention
- Sniff magic bytes (mimetype.Detect) instead of trusting file extensions or Content-Type headers
- Verify assets open in a viewer / pass `file` before feeding them to AI features
- Re-sync or re-download truncated assets after sync interruptions
- Convert HEIC/AVIF photos to JPEG before use
When it happens
Trigger: Passing image bytes whose header does not parse as the declared image/gif, image/jpeg, image/png, or image/webp format into the image preparation function; DecodeConfig returns a parse error such as 'image: unknown format' or 'unexpected EOF'.
Common situations: An asset file was truncated or corrupted on disk; a URL fetch returned an HTML error page instead of image bytes; a WEBP variant or HEIC/AVIF photo was mislabeled with an allowed MIME type; sync produced a partial file.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- decode image failed
- generated image is empty
- generated image is invalid
- AI editor action must not be empty
- AI editor input is empty
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/a916b85b345ebe0d.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/openai.go:779
func PrepareModelImage(data []byte, maxBytes, maxPixels, maxEdge int) (PreparedImage, error) {
if len(data) == 0 {
return PreparedImage{}, errors.New("image data is empty")
}
if maxBytes > 0 && len(data) > maxBytes {
return PreparedImage{}, fmt.Errorf("image exceeds size limit: %d bytes", maxBytes)
}
mimeType := mimetype.Detect(data).String()
if strings.Contains(mimeType, "svg") || bytes.Contains(bytes.ToLower(data[:min(len(data), 512)]), []byte("<svg")) {
return PreparedImage{}, errors.New("SVG images are not accepted by multimodal models")
}
switch mimeType {
case "image/gif", "image/jpeg", "image/png", "image/webp":
default:
return PreparedImage{}, fmt.Errorf("unsupported image type: %s", mimeType)
}
config, _, err := image.DecodeConfig(bytes.NewReader(data))
if err != nil {
return PreparedImage{}, errors.New("unsupported or invalid image: " + err.Error())
}
if config.Width < 1 || config.Height < 1 || maxPixels > 0 && int64(config.Width)*int64(config.Height) > int64(maxPixels) {
return PreparedImage{}, fmt.Errorf("image exceeds pixel limit: %d", maxPixels)
}
decoded, err := imaging.Decode(bytes.NewReader(data), imaging.AutoOrientation(true))
if err != nil {
return PreparedImage{}, errors.New("decode image failed: " + err.Error())
}
bounds := decoded.Bounds()
needsResize := maxEdge > 0 && (bounds.Dx() > maxEdge || bounds.Dy() > maxEdge)
if !needsResize && bounds.Dx() == config.Width && bounds.Dy() == config.Height && mimeType != "image/gif" {
return PreparedImage{
Data: data,
MIMEType: mimeType,
Width: bounds.Dx(),
Height: bounds.Dy(),
SourceSize: len(data),View on GitHub (pinned to 9f775e8a12)