{"record":{"id":"d03bb29d1e67c9f7","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-is-not-accessible","errorCode":null,"errorMessage":"encrypted notebook is not accessible","messagePattern":"encrypted notebook is not accessible","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1678,"sourceCode":"}\n\n// mustEncryptionNonce 从刚刚成功生成的密文中提取 nonce。生成密文格式错误属于内部不变量被破坏，直接终止执行。\nfunc mustEncryptionNonce(ciphertext []byte) []byte {\n\tnonce, err := util.EncryptionNonce(ciphertext)\n\tif err != nil {\n\t\tpanic(\"extract encryption nonce failed: \" + err.Error())\n\t}\n\treturn nonce\n}\n\n// GetDEK 取已缓存的 DEK。返回副本，避免外部零化影响缓存。\n// filesys/assets/db 加解密时调用。\nfunc GetDEK(boxID string) ([]byte, error) {\n\tif !ast.IsNodeIDPattern(boxID) {\n\t\treturn nil, errors.New(\"invalid notebook ID\")\n\t}\n\tif IsEncryptedBox(boxID) && !isBoxUnlockedForAccess(boxID) {\n\t\treturn nil, errors.New(\"encrypted notebook is not accessible\")\n\t}\n\tcachedDEKsLock.RLock()\n\tdefer cachedDEKsLock.RUnlock()\n\tdek, ok := cachedDEKs[boxID]\n\tif !ok {\n\t\treturn nil, errors.New(\"no DEK cached for box \" + boxID)\n\t}\n\tret := make([]byte, len(dek))\n\tcopy(ret, dek)\n\treturn ret, nil\n}\n\n// ClearDEK 清除指定笔记本的 DEK。Unmount 单个加密笔记本时调用。\nfunc ClearDEK(boxID string) {\n\tLockBox(boxID)\n}\n\n// ChangeMasterPassword 改主密码：用旧密码校验后，用新密码派生新 KEK，","sourceCodeStart":1660,"sourceCodeEnd":1696,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1660-L1696","documentation":"The notebook is encrypted but is not currently unlocked for access (isBoxUnlockedForAccess returns false), so its DEK cannot be handed out. This is an access-control check: DEKs for locked encrypted notebooks must never leave the cache, protecting data from unauthorized decryption.","triggerScenarios":"Calling GetDEK for a box where IsEncryptedBox(boxID) is true but the user has not unlocked it in this session (no UnlockEncryptedBox / notebook mounted locked), or after the box was re-locked or the DEK cache was cleared.","commonSituations":"A background job (asset indexing, db sync) running after the user locked the notebook; an operation triggered on a locked encrypted notebook via API; app restart that cleared in-memory DEKs.","solutions":["Unlock the notebook first via the normal unlock flow (prompt the user for the master password), then retry the operation","Reorder logic to skip encrypted-but-locked boxes in background jobs and resume after unlock","Check IsEncryptedBox/isBoxUnlockedForAccess before scheduling work on the box"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if model.IsEncryptedBox(boxID) && !model.IsBoxUnlockedForAccess(boxID) { return errors.New(\"unlock the encrypted notebook before this operation\") }","typeGuard":null,"tryCatchPattern":"dek, err := model.GetDEK(boxID); if err != nil && strings.Contains(err.Error(), \"not accessible\") { /* prompt the user to unlock, then retry */ }","preventionTips":["Check lock state before scheduling background jobs on encrypted boxes","Pause/resume workers around user lock/unlock actions","Never bypass the access check to read keys for locked notebooks"],"tags":["encryption","access-control","locked-notebook"],"backgroundTag":"authentication-required","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}