siyuan-note/siyuan · critical

encrypted notebook lock callbacks are not initialized

Error message

encrypted notebook lock callbacks are not initialized

What it means

holdAVBoxReadLock coordinates read access to attribute views inside encrypted notebooks. If the box is encrypted but the hook callbacks (AVLockAcquire, AVLockRelease, AVIsBoxUnlocked) have not been wired up by the host, it cannot acquire the lock safely and returns this error. This is an initialization/assembly problem, not a data problem.

Source

Thrown at kernel/av/encrypted_hook.go:53

var AVEncryptedBoxIDs func() []string

// AVIsEncryptedBox 由 model 层注入,判断 boxID 是否为加密笔记本。
var AVIsEncryptedBox func(boxID string) bool

// AVIsBoxUnlocked 由 model 层注入,判断加密笔记本是否仍持有 DEK。
var AVIsBoxUnlocked func(boxID string) bool

// AVGetBlockBoxID 由 model 层注入,返回 blockID 所在的 boxID(查 blocktree)。
// 用于镜像写入时校验源块与 AV 定义是否处于同一加密边界。
var AVGetBlockBoxID func(blockID string) string

func holdAVBoxReadLock(boxID string) (release func(), err error) {
	release = func() {}
	if boxID == "" || AVIsEncryptedBox == nil || !AVIsEncryptedBox(boxID) {
		return
	}
	if AVLockAcquire == nil || AVLockRelease == nil || AVIsBoxUnlocked == nil {
		return nil, errors.New("encrypted notebook lock callbacks are not initialized")
	}
	AVLockAcquire(boxID)
	if !AVIsBoxUnlocked(boxID) {
		AVLockRelease(boxID)
		return nil, errors.New("encrypted notebook is locked, please unlock it first")
	}
	return func() {
		AVLockRelease(boxID)
	}, nil
}

// pendingAVBox 记录首次创建的 AV 归属哪个加密 box。
// handler 层创建 AV 前调 SetAVBoxID(avID, boxID),SaveAttributeView 时
// findAttributeViewPath 会先查 pending 映射,找到则写入对应加密笔记本路径。
var pendingAVBox = map[string]string{}
var pendingAVBoxLock = sync.RWMutex{}

// SetAVBoxID 预设 AV 定义的归属 box。加密笔记本创建 AV 时调用,boxID 为空时清理映射。

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Register the lock callbacks (AVLockAcquire, AVLockRelease, AVIsBoxUnlocked) during kernel boot before any AV access
  2. Ensure the code path that initializes encrypted-notebook hooks runs before loading documents
  3. If hooks are intentionally unavailable, treat the box as inaccessible rather than bypassing the lock

Example fix

// before
av.AVIsEncryptedBox = isEncrypted // only partial wiring
// after
av.AVIsEncryptedBox = isEncrypted
av.AVLockAcquire = lockSvc.Acquire
av.AVLockRelease = lockSvc.Release
av.AVIsBoxUnlocked = lockSvc.IsUnlocked
Defensive patterns

Strategy: validation

Validate before calling

if isEncryptedBox(boxID) && (AVLockAcquire == nil || AVLockRelease == nil || AVIsBoxUnlocked == nil) { failFast('encrypted notebook lock hooks not registered') }

Type guard

func lockHooksReady() bool { return AVIsEncryptedBox != nil && AVLockAcquire != nil && AVLockRelease != nil && AVIsBoxUnlocked != nil }

Try / catch

release, err := holdAVBoxReadLock(boxID)
if err != nil {
    if strings.Contains(err.Error(), "callbacks are not initialized") { initEncryptedHooks(); release, err = holdAVBoxReadLock(boxID) }
    if err != nil { return err }
}
defer release()

Prevention

When it happens

Trigger: Accessing an attribute view in an encrypted notebook (loadAttributeViewSearchInfoInBox, parseAttributeViewByPathInBoxWithOptions) while AVIsEncryptedBox reports the box encrypted but AVLockAcquire/AVLockRelease/AVIsBoxUnlocked are nil.

Common situations: Embedding the kernel without calling the setup that registers encrypted-notebook lock hooks; mobile/bindings builds where hook registration was skipped; ordering bug where AV access happens before hook registration.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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