siyuan-note/siyuan · error

empty HEIF image

Error message

empty HEIF image

What it means

convert re-checks that the source byte slice is non-empty before decoding, failing with 'empty HEIF image'. This is the lower-level duplicate of the GetOrCreate guard: even direct callers of convert must supply actual image bytes. The checks in convert run in order: emptiness, size limit (ErrInputTooLarge), then mode validity (ErrInvalidMode).

Source

Thrown at kernel/heif/convert.go:103

func pixelsWithinBudget(workingBudget, outputReserve, hardLimit int) int {
	available := workingBudget - 2*MaxInputBytes - outputReserve
	if available <= 0 {
		return 0
	}
	return min(available/workingBytesPerPixel, hardLimit)
}

type Mode string

const (
	ModePreview   Mode = "preview"
	ModeThumbnail Mode = "thumb"
)

func convert(ctx context.Context, source []byte, mode Mode) ([]byte, error) {
	if len(source) == 0 {
		return nil, errors.New("empty HEIF image")
	}
	if len(source) > MaxInputBytes {
		return nil, ErrInputTooLarge
	}
	if mode != ModePreview && mode != ModeThumbnail {
		return nil, ErrInvalidMode
	}

	select {
	case conversionSlots <- struct{}{}:
		defer func() {
			<-conversionSlots
		}()
	case <-ctx.Done():
		return nil, ctx.Err()
	}
	conversionContext, cancel := context.WithTimeout(ctx, conversionTimeout)
	defer cancel()

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Validate len(source) > 0 in the caller before dispatching conversion work to the goroutine.
  2. Propagate read errors: never send the buffer downstream when the file/asset read failed.
  3. Route all conversion through heif.GetOrCreate so the unified validation (mode, BoxID, emptiness, size) runs first.

Example fix

// before
go func() { out, err := convert(ctx, buf, mode) }() // buf may be empty
// after
if len(buf) == 0 {
    errCh <- errors.New("skip conversion: empty source")
    return
}
go func() { out, err := convert(ctx, buf, mode) }()
Defensive patterns

Strategy: validation

Validate before calling

if len(buf) == 0 {
    return errors.New("refusing conversion: source buffer is empty")
}
if len(buf) > heif.MaxInputBytes {
    return heif.ErrInputTooLarge
}

Try / catch

out, err := convert(ctx, buf, mode)
if err != nil && strings.Contains(err.Error(), "empty HEIF image") {
    return fmt.Errorf("conversion skipped: no image bytes provided: %w", err)
}

Prevention

When it happens

Trigger: Calling the internal convert(ctx, source, mode) with nil/empty source — e.g. an anonymous caller wrapping convert in a goroutine that received an empty buffer from a failed read or an upstream GetOrCreate path whose validation was bypassed.

Common situations: Worker/goroutine pipelines where an empty read result is forwarded to convert without checking len; refactors that add a new entry point into convert without the GetOrCreate pre-validation.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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