siyuan-note/siyuan · error

decode HEIF image

Error message

decode HEIF image: %w

What it means

decodeImage wraps a failure from goheic.DecodeBytes (full pixel decode) as "decode HEIF image: %w", preserving the cause via %w. DecodeConfig succeeded (dimensions valid), but the actual lossy/lossless decode failed — e.g. frame size limit exceeded at full decode, unsupported codec profile, or decoder thread/alloc failure. Raised to convert and ImageSize callers.

Solutions

  1. Read the wrapped cause (errors.Unwrap) to distinguish profile-unsupported vs corrupt data
  2. Re-encode the image to a standard 8-bit HEIC or JPEG using an external tool and re-import
  3. Retry once in case of transient memory/alloc failure; if persistent, mark the asset corrupt

Example fix

// before
result, err := heif.GetOrCreate(ctx, data, opts)
if err != nil { return err }
// after
result, err := heif.GetOrCreate(ctx, data, opts)
if err != nil {
	if strings.Contains(err.Error(), "decode HEIF image:") {
		return fmt.Errorf("HEIF decode failed (unsupported profile or corrupt data): %v", err)
	}
	return err
}
Defensive patterns

Strategy: try-catch

Try / catch

result, err := heif.GetOrCreate(ctx, data, opts)
if err != nil {
	if strings.Contains(err.Error(), "decode HEIF image:") && !strings.Contains(err.Error(), "dimensions") {
		// full decode failed after header ok: unsupported profile or corrupt payload
	}
	return err
}

Prevention

When it happens

Trigger: goheic.DecodeBytes returns error for a container whose header parsed fine: unsupported HEVC/AV1 profile, decoder-internal frame size limit hit despite validDimensions, corrupted tile data after the header.

Common situations: Exotic HEIF profiles (10-bit HDR, image sequences, aux images) the decoder rejects, bit-rot or bit-flips in compressed payload, decoder resource limits under memory pressure.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at kernel/heif/convert.go:198

			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)
	}
	return img, nil
}

func validDimensions(width, height int) bool {
	return width > 0 && height > 0 && width <= maxDimension && height <= maxDimension &&
		uint64(width)*uint64(height) <= uint64(maxPixels)
}

func estimatedJPEGSize(img image.Image) int {
	pixels := int64(img.Bounds().Dx()) * int64(img.Bounds().Dy())
	estimate := pixels / 2
	if estimate < 64*1024 {
		estimate = 64 * 1024
	}
	if estimate > 32*1024*1024 {
		estimate = 32 * 1024 * 1024
	}

View on GitHub (pinned to 9f775e8a12)