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
- Reduce peak memory: lower maxPixels and maxEdge so decode+encode buffers stay small
- Serialize image preparation (worker pool of 1-2) instead of parallel calls
- Raise the container/host memory limit if this is a deployment constraint
- 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
- Cap maxPixels/maxEdge well under memory limits (decoded bitmap is W*H*4 bytes)
- Serialize image preparation rather than running many in parallel
- Monitor kernel memory; encode failures here are usually OOM in disguise
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
- decode image failed:
- unsupported or invalid image:
- encode image failed
- image data is empty
- image exceeds size limit
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)