{"record":{"id":"4cd006a0eb668e78","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-is-locked-please-unlock-it-fir-4cd006","errorCode":null,"errorMessage":"encrypted notebook is locked, please unlock it first","messagePattern":"encrypted notebook is locked, please unlock it first","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/av/encrypted_hook.go","lineNumber":58,"sourceCode":"// 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 为空时清理映射。\n// 普通笔记本不需要调（AV 默认走全局路径）。\nfunc SetAVBoxID(avID, boxID string) {\n\tpendingAVBoxLock.Lock()\n\tdefer pendingAVBoxLock.Unlock()\n\tif boxID != \"\" {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/av/encrypted_hook.go#L40-L76","documentation":"Thrown by holdAVBoxReadLock when an Attribute View belongs to an encrypted notebook whose DEK is no longer cached (the box was locked, or the auto-lock timer fired). The lock was acquired but AVIsBoxUnlocked then returned false, so reading the AV — which requires the DEK to decrypt — is refused to avoid returning ciphertext or stale plaintext.","triggerScenarios":"Any AV read on an encrypted box races with a lock event. Concrete call sites: parseAttributeViewByPathInBox (av.go:788) and loadAttributeViewSearchInfoInBox (av.go:631), both invoked while the editor, search index, or sync is touching an AV inside a notebook the user just locked.","commonSituations":"User clicks 'Lock notebook' (or auto-lock trips) while a database view is open or being indexed; sync triggers re-encryption mid-read; long-running export touches encrypted AVs after the session expired.","solutions":["Unlock the notebook in the UI, then retry the operation (the kernel re-caches the DEK).","Raise or disable the auto-lock timeout if this recurs during long exports.","Avoid concurrent lock-and-read: pause sync/export before locking a notebook that owns AVs."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before performing an AV read on a possibly-encrypted box, check unlock state.\nfunc canReadAV(boxID string) bool {\n    if av.AVIsEncryptedBox == nil || !av.AVIsEncryptedBox(boxID) {\n        return true // plain notebook\n    }\n    if av.AVIsBoxUnlocked == nil {\n        return false // encrypted but callbacks not ready\n    }\n    return av.AVIsBoxUnlocked(boxID)\n}","typeGuard":null,"tryCatchPattern":"// Treat a lock error as a recoverable precondition failure.\nrelease, err := av.HoldAVBoxReadLock(boxID) // exported wrapper if available\nif err != nil {\n    // prompt the user to unlock, then retry the original operation\n    return err\n}\ndefer release()","preventionTips":["Check av.AVIsBoxUnlocked(boxID) before any operation that touches an encrypted notebook's AV.","Do not run long exports against encrypted AVs while auto-lock is enabled.","Pause sync before manually locking a notebook that owns databases."],"tags":["encryption","av","lock","notebook","dek","concurrency"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}