{"record":{"id":"8d171416c206c457","repo":"siyuan-note/siyuan","slug":"conf-language-314-8d1714","errorCode":null,"errorMessage":"Conf.Language(314)","messagePattern":"Conf\\.Language\\(314\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/repository.go","lineNumber":298,"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":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/repository.go#L280-L316","documentation":"RollbackRepoSnapshotFile refuses to roll back a snapshot file that belongs to an encrypted notebook when that notebook is not currently mounted. If the notebook were not mounted, getRollbackBox would fall back to a plain (unencrypted) notebook and WriteTree would write the decrypted .sy content to disk as plaintext, violating the encrypted-notebook guarantee that decrypted data never crosses the encryption boundary. The error text comes from i18n key 314.","triggerScenarios":"Calling RollbackRepoSnapshotFile (via API repo/rollbackRepoFile) where the snapshot path resolves to origBoxID with IsEncryptedBox(origBoxID)==true while Conf.Box(origBoxID) returns nil — i.e. the encrypted notebook exists in the snapshot but is closed/not mounted in the workspace at rollback time.","commonSituations":"User closed the encrypted notebook (or it failed to mount at boot) and then tries to roll back one of its documents from the repo snapshot history; workspace restored from backup where notebooks.json no longer lists the encrypted notebook; encrypted notebook deleted but its snapshots still exist in the repo.","solutions":["Open/mount the encrypted notebook in the SiYuan UI (or ensure it appears in the workspace notebook list) before rolling back the snapshot file","Restart the kernel so all configured notebooks are mounted, then retry the rollback","Verify the snapshot's boxID matches an existing notebook ID and that the notebook was not deleted","If the notebook is intentionally gone, remove the stale snapshots instead of rolling back into it"],"exampleFix":"// before (fails: notebook closed)\nRollbackRepoSnapshotFile(fileID)\n\n// after: ensure the box is mounted first\nbox := Conf.Box(origBoxID)\nif box == nil {\n    // open/mount the notebook via the UI or OpenBox API, then retry\n    RollbackRepoSnapshotFile(fileID)\n}","handlingStrategy":"validation","validationCode":"if !IsEncryptedBox(origBoxID) || Conf.Box(origBoxID) != nil {\n    RollbackRepoSnapshotFile(fileID) // safe to proceed\n}","typeGuard":null,"tryCatchPattern":"if err := rollback(fileID); err != nil && strings.Contains(err.Error(), Conf.Language(314)) {\n    // prompt user to mount the encrypted notebook, then retry\n}","preventionTips":["Check Conf.Box(boxID) != nil before any repo rollback on an encrypted notebook","Keep encrypted notebooks mounted (not closed) while performing history/rollback operations","After workspace restore or migration, confirm notebooks.json lists all encrypted notebooks before touching repo history"],"tags":["go","repository","encryption","rollback","notebook"],"backgroundTag":"invalid-state-transition","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}