siyuan-note/siyuan · error

encrypted HEIF cache requires a notebook ID

Error message

encrypted HEIF cache requires a notebook ID

What it means

The HEIF cache refuses to operate on encrypted images when the Options do not specify a notebook ID. For encrypted notebooks, cache keys and storage must be namespaced per notebook so plaintext previews never leak across notebooks; a missing BoxID would make the cache key ambiguous or unsafe, so GetOrCreate rejects the call up front.

Source

Thrown at kernel/heif/cache.go:92

	memoryCacheItems = map[string]*list.Element{}
	memoryCacheBytes int
)

func IsPath(path string) bool {
	path = strings.ToLower(path)
	if index := strings.IndexAny(path, "?#"); index >= 0 {
		path = path[:index]
	}
	ext := filepath.Ext(path)
	return ext == ".heic" || ext == ".heif"
}

func GetOrCreate(ctx context.Context, source []byte, options Options) (Result, error) {
	if options.Mode != ModePreview && options.Mode != ModeThumbnail {
		return Result{}, ErrInvalidMode
	}
	if options.Encrypted && options.BoxID == "" {
		return Result{}, errors.New("encrypted HEIF cache requires a notebook ID")
	}
	if len(source) == 0 {
		return Result{}, errors.New("empty HEIF image")
	}
	if len(source) > MaxInputBytes {
		return Result{}, ErrInputTooLarge
	}
	if err := ctx.Err(); err != nil {
		return Result{}, err
	}

	digest := cacheDigest(source, options.Mode)
	etag := `"heif-` + digest + `"`
	cacheKey := digest
	if options.Encrypted {
		cacheKey = options.BoxID + ":" + digest
		if data := getMemoryCache(cacheKey); data != nil {
			return Result{Data: data, ETag: etag}, nil

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Set Options.BoxID to the owning notebook's ID whenever Options.Encrypted is true.
  2. Only set Encrypted=true when the source image really comes from an encrypted notebook; otherwise keep Encrypted=false.
  3. Construct Options through a helper that derives Encrypted/BoxID together from the asset's notebook context to keep them consistent.

Example fix

// before
opts := heif.Options{Mode: heif.ModePreview, Encrypted: true} // BoxID missing
res, err := heif.GetOrCreate(ctx, data, opts) // error
// after
opts := heif.Options{Mode: heif.ModePreview, Encrypted: true, BoxID: "20240101120000-abcdefg"}
res, err := heif.GetOrCreate(ctx, data, opts)
Defensive patterns

Strategy: validation

Validate before calling

if options.Encrypted && options.BoxID == "" {
    return errors.New("cannot build HEIF cache options: encrypted image requires BoxID")
}

Try / catch

res, err := heif.GetOrCreate(ctx, data, opts)
if errors.Is(err, errors.New("encrypted HEIF cache requires a notebook ID")) || err != nil && strings.Contains(err.Error(), "requires a notebook ID") {
    return fmt.Errorf("fix Options: set BoxID for encrypted image: %w", err)
}

Prevention

When it happens

Trigger: Calling heif.GetOrCreate with Options{Encrypted: true} while leaving Options.BoxID empty. Detected before any decoding happens, alongside the mode and emptiness checks.

Common situations: Callers building Options programmatically and forgetting to propagate the notebook ID; refactoring that made Encrypted default to true but left BoxID unset; test harnesses constructing partial Options.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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