siyuan-note/siyuan · error

ErrInvalidMode

ErrInvalidMode

Error message

invalid HEIF conversion mode

What it means

ErrInvalidMode is returned when Options.Mode is neither ModePreview nor ModeThumbnail. GetOrCreate validates the mode before decoding, and convert re-validates it. It is a programming/API-misuse sentinel: only the two exported Mode values are accepted.

Source

Thrown at kernel/heif/convert.go:54

	previewQuality   = 90
	thumbnailQuality = 85
	MaxInputBytes    = 32 * 1024 * 1024
	maxDimension     = 65535
	desktopMaxPixels = 50_000_000
	mobileMaxPixels  = 12_500_000
	// 解码层仅接受至多 10-bit 4:2:0、无透明通道的单 slice 图像;这里按解码面、CTU 表、
	// NRGBA 和一次合并变换预留每像素 16 字节,再单独预留源文件、RBSP 副本与 JPEG 输出。
	workingBytesPerPixel = 16
	desktopWorkingBudget = 960 * 1024 * 1024
	mobileWorkingBudget  = 320 * 1024 * 1024
	desktopOutputReserve = 96 * 1024 * 1024
	mobileOutputReserve  = 32 * 1024 * 1024
)

var (
	ErrInputTooLarge = errors.New("HEIF image exceeds the input size limit")
	ErrImageTooLarge = errors.New("HEIF image exceeds the dimension limit")
	ErrInvalidMode   = errors.New("invalid HEIF conversion mode")

	conversionSlots = make(chan struct{}, 1)
	maxPixels       = platformMaxPixels()
)

func platformMaxPixels() int {
	if runtime.GOOS == "android" || runtime.GOOS == "ios" {
		return pixelsWithinBudget(mobileWorkingBudget, mobileOutputReserve, mobileMaxPixels)
	}
	return pixelsWithinBudget(desktopWorkingBudget, desktopOutputReserve, desktopMaxPixels)
}

func ReadFileLimited(path string, maxBytes int64) ([]byte, error) {
	if maxBytes <= 0 {
		return nil, ErrInputTooLarge
	}
	file, err := os.Open(path)
	if err != nil {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Always set Options.Mode to heif.ModePreview or heif.ModeThumbnail using the exported constants, never raw strings.
  2. If Options is assembled dynamically, default to ModePreview when unset before calling GetOrCreate.
  3. Check string case and whitespace if the mode is derived from configuration; compare against the constants, not literals.

Example fix

// before
opts := heif.Options{Encrypted: true, BoxID: box} // Mode zero-value
res, err := heif.GetOrCreate(ctx, data, opts) // ErrInvalidMode
// after
opts := heif.Options{Mode: heif.ModeThumbnail, Encrypted: true, BoxID: box}
res, err := heif.GetOrCreate(ctx, data, opts)
Defensive patterns

Strategy: type-guard

Validate before calling

func validMode(m heif.Mode) bool { return m == heif.ModePreview || m == heif.ModeThumbnail }
if !validMode(opts.Mode) { return errors.New("Options.Mode must be heif.ModePreview or heif.ModeThumbnail") }

Type guard

func isConversionMode(m heif.Mode) bool {
    return m == heif.ModePreview || m == heif.ModeThumbnail
}

Try / catch

res, err := heif.GetOrCreate(ctx, data, opts)
if errors.Is(err, heif.ErrInvalidMode) {
    opts.Mode = heif.ModePreview // safe default
    res, err = heif.GetOrCreate(ctx, data, opts)
}

Prevention

When it happens

Trigger: Calling heif.GetOrCreate with a zero-value Options{} (Mode is the empty string), or with a custom/typo'd mode string such as "full" or "Preview" (case-sensitive). Test TestGetOrCreateValidatesOptionsBeforeDecode relies on exactly this sentinel.

Common situations: Constructing Options{} partially and forgetting Mode; copying Options from a struct literal that omitted Mode; writing custom mode strings instead of using the heif.ModePreview/ModeThumbnail constants.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/15ebc1376a07096d. Report an issue: GitHub.