siyuan-note/siyuan · error

decode HEIF image

Error message

decode HEIF image: %v

What it means

decodeImage recovers a panic from the vendored goheic decoder and converts it into this error ("decode HEIF image: %v" with the recovered value). This keeps malformed or hostile HEIF bitstreams from crashing the kernel with a panic. The %v formatting means the original panic value is not wrapped — match by prefix, not errors.Is.

Solutions

  1. Treat the source file as corrupt: re-export/re-import the image from the original device
  2. Inspect the recovered panic message to identify whether it is a known goheic limitation
  3. Isolate the failing asset and reproduce with goheic directly; report upstream if it is a decoder bug

Example fix

// before
img, err := heif.ImageSize(data) // panics propagate in naive wrappers
// after
if _, err := heif.ImageSize(data); err != nil {
	if strings.HasPrefix(err.Error(), "decode HEIF image:") {
		return fmt.Errorf("asset is corrupt or unsupported HEIF: %v", err)
	}
	return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// heuristically pre-validate the container magic before decode
if !bytes.Contains(data[:12], []byte("ftyp")) {
	return fmt.Errorf("not a HEIF container")
}

Try / catch

if _, err := heif.ImageSize(data); err != nil {
	if strings.HasPrefix(err.Error(), "decode HEIF image:") {
		// decoder panicked on this input; treat as corrupt, never retry blindly
		return fmt.Errorf("corrupt HEIF asset: %v", err)
	}
}

Prevention

When it happens

Trigger: goheic.DecodeBytes (or related code inside decodeImage) panics on a corrupt, truncated, or malformed HEIF payload; the recover in decodeImage fires and returns this error.

Common situations: Fuzzed or corrupted .heic assets, files with valid headers but broken compressed payloads, goheic library bugs on exotic HEIF profiles (e.g. multi-image or depth maps).

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/75c3a455bb78cf36. Report an issue: GitHub.

Appendix: source

Thrown at kernel/heif/convert.go:180

	defer func() {
		<-conversionSlots
	}()
	img, err := decodeImage(source)
	if err != nil {
		return 0, 0, err
	}
	width, height = img.Bounds().Dx(), img.Bounds().Dy()
	if !validDimensions(width, height) {
		return 0, 0, ErrImageTooLarge
	}
	return width, height, nil
}

func decodeImage(source []byte) (img image.Image, err error) {
	defer func() {
		if recovered := recover(); recovered != nil {
			img = nil
			err = fmt.Errorf("decode HEIF image: %v", recovered)
		}
	}()

	config, err := goheic.DecodeConfigBytes(source)
	if err != nil {
		return nil, fmt.Errorf("read HEIF image dimensions: %w", err)
	}
	if !validDimensions(config.Width, config.Height) {
		return nil, ErrImageTooLarge
	}

	img, err = goheic.DecodeBytes(source, goheic.Options{
		AutoRotate:     true,
		FrameSizeLimit: maxPixels,
		Threads:        1,
	})
	if err != nil {
		return nil, fmt.Errorf("decode HEIF image: %w", err)

View on GitHub (pinned to 9f775e8a12)