siyuan-note/siyuan · error

encode image failed:

Error message

encode image failed: 

What it means

Thrown by util.PrepareModelImage (kernel/util/openai.go:758) when re-encoding a decoded/resized PNG or WebP back to PNG fails (png.Encode). This is an internal late-stage failure: by this point the image decoded and was optionally resized, so failure is almost always resource exhaustion (OOM on huge canvases) rather than input format. The stdlib encode error is appended.

Solutions

  1. Reduce peak memory: lower maxPixels and maxEdge so decode+encode buffers stay small
  2. Serialize image preparation (worker pool of 1-2) instead of parallel calls
  3. Raise the container/host memory limit if this is a deployment constraint
  4. Patch PrepareModelImage so a failed PNG encode falls through to the JPEG path instead of returning

Example fix

// before (library: kernel/util/openai.go)
if err = png.Encode(&output, decoded); err != nil {
    return PreparedImage{}, errors.New("encode image failed: " + err.Error())
}

// after
if err = png.Encode(&output, decoded); err != nil {
    // PNG 编码失败时回退到 JPEG 编码,避免整张图片直接失败
    output.Reset()
} else if maxBytes <= 0 || output.Len() <= maxBytes {
    return PreparedImage{Data: output.Bytes(), MIMEType: "image/png", Width: bounds.Dx(), Height: bounds.Dy(), SourceSize: len(data)}, nil
}
Defensive patterns

Strategy: fallback

Validate before calling

// Estimate peak memory before preparing: decoded RGBA needs W*H*4 bytes
if cfg, _, e := image.DecodeConfig(bytes.NewReader(data)); e == nil {
    if int64(cfg.Width)*int64(cfg.Height)*4 > memBudget {
        data = downscaleFirst(data) // reduce before calling PrepareModelImage
    }
}

Try / catch

if err != nil && strings.HasPrefix(err.Error(), "encode image failed:") {
    // resource failure: skip the image or retry once with smaller limits, never hammer-retry
}

Prevention

When it happens

Trigger: Near-limit images (tens of megapixels) on memory-constrained kernels (Docker/WSL limits); concurrent PrepareModelImage calls multiplying peak memory; pathological image content inflating encode buffers.

Common situations: Docker desktop kernels with low memory ceilings processing large scans; several multimodal requests in parallel; 32-bit or ARM hosts with tight address space.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/df43eb163c411d5f. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/openai.go:758

	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
		if err = png.Encode(&output, decoded); err != nil {
			return PreparedImage{}, errors.New("encode image failed: " + err.Error())
		}
		if maxBytes <= 0 || output.Len() <= maxBytes {
			return PreparedImage{
				Data:       output.Bytes(),
				MIMEType:   "image/png",
				Width:      bounds.Dx(),
				Height:     bounds.Dy(),
				SourceSize: len(data),
			}, nil
		}
	}
	var output bytes.Buffer
	if err = jpeg.Encode(&output, decoded, &jpeg.Options{Quality: 92}); err != nil {
		return PreparedImage{}, errors.New("encode image failed: " + err.Error())
	}
	if maxBytes > 0 && output.Len() > maxBytes {
		return PreparedImage{}, fmt.Errorf("prepared image exceeds size limit: %d bytes", maxBytes)
	}

View on GitHub (pinned to afa823b6b4)