{"record":{"id":"dac582fdebc661c6","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-lock-callbacks-are-not-initiali","errorCode":null,"errorMessage":"encrypted notebook lock callbacks are not initialized","messagePattern":"encrypted notebook lock callbacks are not initialized","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/av/encrypted_hook.go","lineNumber":53,"sourceCode":"var AVEncryptedBoxIDs func() []string\n\n// AVIsEncryptedBox 由 model 层注入，判断 boxID 是否为加密笔记本。\nvar AVIsEncryptedBox func(boxID string) bool\n\n// AVIsBoxUnlocked 由 model 层注入，判断加密笔记本是否仍持有 DEK。\nvar AVIsBoxUnlocked func(boxID string) bool\n\n// AVGetBlockBoxID 由 model 层注入，返回 blockID 所在的 boxID（查 blocktree）。\n// 用于镜像写入时校验源块与 AV 定义是否处于同一加密边界。\nvar AVGetBlockBoxID func(blockID string) string\n\nfunc holdAVBoxReadLock(boxID string) (release func(), err error) {\n\trelease = func() {}\n\tif boxID == \"\" || AVIsEncryptedBox == nil || !AVIsEncryptedBox(boxID) {\n\t\treturn\n\t}\n\tif AVLockAcquire == nil || AVLockRelease == nil || AVIsBoxUnlocked == nil {\n\t\treturn nil, errors.New(\"encrypted notebook lock callbacks are not initialized\")\n\t}\n\tAVLockAcquire(boxID)\n\tif !AVIsBoxUnlocked(boxID) {\n\t\tAVLockRelease(boxID)\n\t\treturn nil, errors.New(\"encrypted notebook is locked, please unlock it first\")\n\t}\n\treturn func() {\n\t\tAVLockRelease(boxID)\n\t}, nil\n}\n\n// pendingAVBox 记录首次创建的 AV 归属哪个加密 box。\n// handler 层创建 AV 前调 SetAVBoxID(avID, boxID)，SaveAttributeView 时\n// findAttributeViewPath 会先查 pending 映射，找到则写入对应加密笔记本路径。\nvar pendingAVBox = map[string]string{}\nvar pendingAVBoxLock = sync.RWMutex{}\n\n// SetAVBoxID 预设 AV 定义的归属 box。加密笔记本创建 AV 时调用，boxID 为空时清理映射。","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/av/encrypted_hook.go#L35-L71","documentation":"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.","triggerScenarios":"Accessing an attribute view in an encrypted notebook (loadAttributeViewSearchInfoInBox, parseAttributeViewByPathInBoxWithOptions) while AVIsEncryptedBox reports the box encrypted but AVLockAcquire/AVLockRelease/AVIsBoxUnlocked are nil.","commonSituations":"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.","solutions":["Register the lock callbacks (AVLockAcquire, AVLockRelease, AVIsBoxUnlocked) during kernel boot before any AV access","Ensure the code path that initializes encrypted-notebook hooks runs before loading documents","If hooks are intentionally unavailable, treat the box as inaccessible rather than bypassing the lock"],"exampleFix":"// before\nav.AVIsEncryptedBox = isEncrypted // only partial wiring\n// after\nav.AVIsEncryptedBox = isEncrypted\nav.AVLockAcquire = lockSvc.Acquire\nav.AVLockRelease = lockSvc.Release\nav.AVIsBoxUnlocked = lockSvc.IsUnlocked","handlingStrategy":"validation","validationCode":"if isEncryptedBox(boxID) && (AVLockAcquire == nil || AVLockRelease == nil || AVIsBoxUnlocked == nil) { failFast('encrypted notebook lock hooks not registered') }","typeGuard":"func lockHooksReady() bool { return AVIsEncryptedBox != nil && AVLockAcquire != nil && AVLockRelease != nil && AVIsBoxUnlocked != nil }","tryCatchPattern":"release, err := holdAVBoxReadLock(boxID)\nif err != nil {\n    if strings.Contains(err.Error(), \"callbacks are not initialized\") { initEncryptedHooks(); release, err = holdAVBoxReadLock(boxID) }\n    if err != nil { return err }\n}\ndefer release()","preventionTips":["Register all encrypted-notebook hooks during boot before serving requests","Add a startup assertion that hooks are wired when encryption is enabled","Cover the un-hooked encrypted path in tests"],"tags":["encrypted-notebook","initialization","locking"],"backgroundTag":"missing-dependency","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}