{"record":{"id":"442d86f3e97e4184","repo":"siyuan-note/siyuan","slug":"no-dek-cached-for-box-boxid","errorCode":null,"errorMessage":"no DEK cached for box \" + boxID","messagePattern":"no DEK cached for box \" \\+ boxID","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1684,"sourceCode":"\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，\n// 重新加密 verifier，并把所有加密笔记本的 WrappedDEK 用新 KEK 重新包络后写回各自的 BoxConf。\n//\n// 使用两阶段提交确保崩溃后可恢复：\n//\n//\tPhase 0: 预计算所有新 WrappedDEK（内存）\n//\tPhase 1: 写入 migration manifest","sourceCodeStart":1666,"sourceCodeEnd":1702,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1666-L1702","documentation":"GetDEK found no DEK cached for the given (valid, accessible) box ID. A DEK is only in cachedDEKs while the encrypted notebook is unlocked; for unencrypted boxes the DEK is populated when the notebook is loaded. Absence means the key was never loaded or was evicted/zeroed.","triggerScenarios":"Calling GetDEK before the notebook's key-loading path ran (e.g. very early at boot), after LockEncryptedBox zeroed and removed the entry, after ChangeMasterPassword cleared caches, or with a box ID that has no loaded session.","commonSituations":"Race between a background worker and the user locking a notebook; calling encryption helpers from a plugin/API context without mounting the notebook; startup ordering issues.","solutions":["Ensure the notebook is loaded/unlocked (DEK populated) before calling GetDEK; verify the box ID spelling matches the loaded notebook","Add retry/re-check after unlock completes rather than assuming the cache is warm","If it happens at boot, move the work to after workspace/notebook initialization completes"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// ensure the notebook is loaded/unlocked first\nif !boxLoaded(boxID) { return errors.New(\"load or unlock the notebook before requesting its DEK\") }","typeGuard":null,"tryCatchPattern":"dek, err := model.GetDEK(boxID); if err != nil && strings.Contains(err.Error(), \"no DEK cached\") { /* reload/unlock the notebook, then retry once */ }","preventionTips":["Initialize notebooks before running encryption-dependent jobs","Re-fetch the DEK after every lock/unlock or password change instead of caching it long-term","Avoid races: coordinate workers with the notebook lifecycle events"],"tags":["encryption","cache","key-management"],"backgroundTag":"record-not-found","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"}