{"record":{"id":"0f9c2b0f382ef2b3","repo":"siyuan-note/siyuan","slug":"please-unlock-the-encrypted-notebook-first-0f9c2b","errorCode":null,"errorMessage":"Please unlock the encrypted notebook first","messagePattern":"Please unlock the encrypted notebook first","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/repository.go","lineNumber":315,"sourceCode":"\tif err = os.WriteFile(from, data, 0644); nil != err {\n\t\tlogging.LogErrorf(\"write file [%s] failed: %v\", filepath.Join(tempRepoDiffDir, file.Path), err)\n\t\treturn\n\t}\n\t// 解密后的临时文件在函数返回时清理，避免加密文档明文残留在磁盘\n\tdefer os.Remove(from)\n\n\tif strings.HasSuffix(file.Path, \".sy\") {\n\t\tboxID := strings.TrimPrefix(file.Path, \"/\")\n\t\tboxID = strings.Split(boxID, \"/\")[0]\n\t\torigBoxID := boxID // 保留原始 boxID 用于加密边界校验\n\n\t\t// 加密笔记本的快照回滚要求原笔记本已挂载：\n\t\t// WriteTree 根据 tree.Box 判断是否加密落盘。若原笔记本未挂载导致\n\t\t// getRollbackBox fallback 到普通 Rollback 笔记本，解密后的 .sy 将被 WriteTree\n\t\t// 以明文落盘，违反加密笔记本\"数据不跨边界\"的约束。\n\t\tif IsEncryptedBox(origBoxID) && nil == Conf.Box(origBoxID) {\n\t\t\tlogging.LogErrorf(\"rollback encrypted repo snapshot requires notebook [%s] to be mounted\", origBoxID)\n\t\t\terr = errors.New(Conf.Language(314))\n\t\t\treturn\n\t\t}\n\n\t\tvar box *Box\n\t\tvar needResetTree bool\n\t\tbox, needResetTree, err = getRollbackBox(boxID)\n\t\tif err != nil {\n\t\t\tlogging.LogErrorf(\"get rollback box [%s] failed: %s\", boxID, err)\n\t\t\treturn\n\t\t}\n\t\tboxID = box.ID\n\n\t\tvar destPath, parentHPath string\n\t\trootID := util.GetTreeID(file.Path)\n\t\tworkingDoc := treenode.GetBlockTree(rootID)\n\t\tif needResetTree {\n\t\t\tworkingDoc = nil\n\t\t}","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/repository.go#L297-L333","documentation":"Thrown by RollbackRepoSnapshotFile() via Conf.Language(314) which resolves to 'Please unlock the encrypted notebook first'. When rolling back a .sy file that belongs to an encrypted notebook, the code checks IsEncryptedBox(origBoxID) and Conf.Box(origBoxID). If the notebook is encrypted but not currently mounted (Conf.Box returns nil), rollback is refused because WriteTree would fall back to a non-encrypted box and write decrypted content as plaintext, violating the encryption boundary.","triggerScenarios":"Calling RollbackRepoSnapshotFile(fileID) where the file path starts with an encrypted notebook ID, and that notebook is not currently mounted/unlocked (Conf.Box(boxID) returns nil). The encrypted notebook must be mounted so WriteTree can correctly persist the decrypted content back as encrypted.","commonSituations":"The user locked the encrypted notebook (or it auto-locked) and then tried to roll back a file from the data repo history. The encrypted notebook was never mounted in the current session. The user is browsing repo history from the global view without unlocking the specific encrypted notebook first.","solutions":["Unlock/mount the encrypted notebook in the SiYuan UI before attempting the rollback.","In the UI/API layer, check Conf.Box(boxID) != nil for encrypted notebooks before enabling the rollback action.","After unlocking, retry the rollback operation."],"exampleFix":"// before — encrypted notebook not mounted\nerr := model.RollbackRepoSnapshotFile(fileID)\n\n// after — unlock the notebook first\nbox := model.Conf.Box(origBoxID)\nif model.IsEncryptedBox(origBoxID) && box == nil {\n    util.PushMsg(\"Please unlock the encrypted notebook first\", 7000)\n    return\n}\nerr := model.RollbackRepoSnapshotFile(fileID)","handlingStrategy":"validation","validationCode":"// For encrypted notebooks, verify the notebook is mounted before rollback\nif IsEncryptedBox(boxID) && Conf.Box(boxID) == nil {\n    return errors.New(\"please unlock and mount the encrypted notebook before rolling back files\")\n}\nerr := model.RollbackRepoSnapshotFile(fileID)","typeGuard":"func isEncryptedBoxMounted(boxID string) bool {\n    if !IsEncryptedBox(boxID) {\n        return true // not encrypted, always OK\n    }\n    return Conf.Box(boxID) != nil\n}","tryCatchPattern":null,"preventionTips":["Unlock encrypted notebooks before accessing their repo history.","In the UI, check notebook mount status before enabling rollback for encrypted notebook files.","Show a clear prompt directing the user to unlock the notebook first."],"tags":["data-repo","encryption","rollback","encrypted-notebook","security","guard"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}