{"record":{"id":"a05d7c9b15e9fc3d","repo":"siyuan-note/siyuan","slug":"no-dek-cached-for-box","errorCode":null,"errorMessage":"no DEK cached for box ","messagePattern":"no DEK cached for box ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","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/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto.go#L1666-L1702","documentation":"The box ID is valid and the notebook is accessible, but no DEK is present in the in-memory `cachedDEKs` map. The DEK cache is populated only when an encrypted notebook is unlocked; a non-encrypted notebook or one whose unlock was lost (restart) has no cached key.","triggerScenarios":"GetDEK on a notebook after kernel restart (cache empty), on a non-encrypted notebook, or when the cache entry was evicted/zeroed while the notebook is still considered mounted.","commonSituations":"Long-running background task holding a box reference across a lock/unlock cycle; accessing notebooks before unlock at startup; race between LockBox (cache wipe) and an in-flight GetDEK.","solutions":["Unlock the encrypted notebook to repopulate the DEK cache, then retry.","For non-encrypted notebooks, do not call GetDEK — encryption is not in play.","Re-check lock state after any error and serialize lock/unlock operations against background readers.","If this happens right after restart, gate encryption-dependent work until unlock flow completes."],"exampleFix":"// before\ndek, err := GetDEK(boxID)\n// after\ndek, err := GetDEK(boxID)\nif err != nil && strings.HasPrefix(err.Error(), \"no DEK cached\") {\n    return promptUnlockAndRetry(boxID) // unlock repopulates cache\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"dek, err := GetDEK(boxID); if err != nil && strings.HasPrefix(err.Error(), \"no DEK cached\") { unlockBox(boxID); dek, err = GetDEK(boxID) }","preventionTips":["Unlock encrypted notebooks before running encryption-dependent tasks after restart","Serialize lock/unlock against background readers to avoid cache races","Skip GetDEK entirely for non-encrypted notebooks"],"tags":["encryption","key-cache","locked-notebook"],"backgroundTag":"record-not-found","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"}