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}, nilView on GitHub (pinned to 8641553a1f)
Solutions
- Set Options.BoxID to the owning notebook's ID whenever Options.Encrypted is true.
- Only set Encrypted=true when the source image really comes from an encrypted notebook; otherwise keep Encrypted=false.
- 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
- Build Options through a single constructor that always pairs Encrypted with BoxID.
- Pass the notebook ID down through the asset-processing call chain explicitly.
- Add a unit test asserting every Options built in encrypted mode carries a non-empty BoxID.
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
- source is not an encrypted asset
- invalid .sy base name [%s]: must end with .sy
- invalid .sy base name [%s]: stem is not a node ID
- empty HEIF image
- empty HEIF image
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/7dac842b12387c86.
Report an issue: GitHub.