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

  1. Re-export or re-download the source image (the bytes are corrupt past the header)
  2. Round-trip through a tolerant encoder (magick in.jpg out.jpg) to rebuild the pixel stream
  3. In batch flows, catch this error and skip the asset instead of aborting
  4. 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

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


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.Buffer

View on GitHub (pinned to afa823b6b4)