siyuan-note/siyuan · error
decode image failed:
Error message
decode image failed:
What it means
Thrown by util.PrepareModelImage (kernel/util/openai.go:738) when full-pixel decoding via imaging.Decode (with AutoOrientation) fails even though image.DecodeConfig succeeded on the same bytes. That means the header parsed but the pixel data or EXIF orientation chunk is unreadable, or the body is truncated after a valid header. The imaging error text is appended.
Solutions
- Re-export or re-download the source image (the bytes are corrupt past the header)
- Round-trip through a tolerant encoder (magick in.jpg out.jpg) to rebuild the pixel stream
- In batch flows, catch this error and skip the asset instead of aborting
- If reproducible with one file, verify with an external decoder to confirm the asset itself is bad
Example fix
// before
prepared, err := util.PrepareModelImage(data, maxBytes, maxPixels, maxEdge)
if err != nil { return err }
// after
prepared, err := util.PrepareModelImage(data, maxBytes, maxPixels, maxEdge)
if err != nil && strings.HasPrefix(err.Error(), "decode image failed") {
data = reencodeExternally(data) // rebuild pixel stream
prepared, err = util.PrepareModelImage(data, maxBytes, maxPixels, maxEdge)
} Defensive patterns
Strategy: validation
Validate before calling
if _, err := imaging.Decode(bytes.NewReader(data), imaging.AutoOrientation(true)); err != nil {
// full decode will fail too: re-export the source file now
data = reencodeExternally(data)
} Try / catch
if err != nil && strings.HasPrefix(err.Error(), "decode image failed:") {
// header parsed but pixels are corrupt: re-export or skip; retrying unchanged cannot help
} Prevention
- Avoid reading assets while another process is still writing them
- Re-sync corrupted assets from the original source
- Skip undecodable assets per-image so one bad file does not abort batch analysis
When it happens
Trigger: File truncated mid-body (valid header, cut IDAT/JPEG scan); corrupt pixel stream; EXIF orientation tag malformed so AutoOrientation fails; rare decoder disagreement between header parse and full decode.
Common situations: Partial uploads/syncs, assets written while still being encoded by another process, images that open in lenient viewers but fail strict Go decoders.
Related errors
- unsupported or invalid image:
- encode image failed:
- decode existing session data failed
- decode session data failed
- encrypted notebook has no valid key material
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/aebc3098ba5a965f.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/openai.go:738
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),
}, nil
}
if needsResize {
decoded = imaging.Fit(decoded, maxEdge, maxEdge, imaging.Lanczos)
}
bounds = decoded.Bounds()
if mimeType == "image/png" || mimeType == "image/webp" {
var output bytes.BufferView on GitHub (pinned to afa823b6b4)