{"record":{"id":"69b7c96dbb7239a9","repo":"siyuan-note/siyuan","slug":"conf-language-315","errorCode":null,"errorMessage":"Conf.Language(315)","messagePattern":"Conf\\.Language\\(315\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":283,"sourceCode":"\ttmpPath := path + \".\" + gulu.Rand.String(7) + \".tmp\"\n\tif err := os.WriteFile(tmpPath, data, 0644); err != nil {\n\t\treturn err\n\t}\n\treturn os.Rename(tmpPath, path)\n}\n\n// ExportNotebookCryptoBackup 把密钥备份文件复制到 export 目录，返回可下载的相对路径。\n// 供用户主动导出保存，作为同步之外的独立恢复途径（详见设计文档 §4.1）。\n// 备份文件本身不含主密码（salt 不保密、verifier 是密文），拿到它也解不开任何数据。\nfunc ExportNotebookCryptoBackup() (downloadPath string, err error) {\n\tnotebookCryptoMu.Lock()\n\tdefer notebookCryptoMu.Unlock()\n\n\tbackupPath := dataCryptoBackupPath()\n\tdata, readErr := filelock.ReadFile(backupPath)\n\tif readErr != nil {\n\t\tif os.IsNotExist(readErr) {\n\t\t\terr = errors.New(Conf.Language(315))\n\t\t\treturn\n\t\t}\n\t\terr = readErr\n\t\treturn\n\t}\n\texportBase := filepath.Join(util.TempDir, \"export\")\n\tif mkErr := os.MkdirAll(exportBase, 0755); mkErr != nil {\n\t\terr = mkErr\n\t\treturn\n\t}\n\t// 用随机名避免不同用户/设备互相覆盖，文件名固定带易识别前缀\n\tfileName := \"notebook-crypto-backup-\" + gulu.Rand.String(7) + \".json\"\n\tdownloadPath = \"/export/\" + url.PathEscape(fileName)\n\tif writeErr := os.WriteFile(filepath.Join(exportBase, fileName), data, 0644); writeErr != nil {\n\t\terr = writeErr\n\t\treturn\n\t}\n\treturn","sourceCodeStart":265,"sourceCodeEnd":301,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/crypto.go#L265-L301","documentation":"ExportNotebookCryptoBackup() copies the global key backup file (<DataDir>/.siyuan/data-crypto-backup.json) into the export directory. If reading it fails with os.IsNotExist, it returns Conf.Language(315): 'Encrypted notebooks already exist but the master key backup is missing. Restore the original conf.json or backup file to re-enable'. The backup holds the MasterSalt/verifier chain; without it a lost conf.json makes existing WrappedDEK undecryptable even with the correct password.","triggerScenarios":"Invoking the export while the backup file was never written (enablement interrupted), deleted by the user or an over-eager cleaner, or excluded/removed by a sync tool that does not carry the .siyuan directory.","commonSituations":"Selective sync (Dropbox/own scripts) skipping dot-directories; restore of conf.json from a snapshot predating encryption enablement; antivirus/cleaner removing unknown .json files.","solutions":["Restore data-crypto-backup.json from dejavu sync history or any file-level backup of the workspace","Alternatively restore the original conf.json that matched the current encrypted notebooks, as the message states","Verify the file exists at <workspace>/data/.siyuan/data-crypto-backup.json and retry the export","After recovery, export the backup immediately and store it outside the workspace"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"backupPath := filepath.Join(util.DataDir, \".siyuan\", \"data-crypto-backup.json\")\nif !filelock.IsExist(backupPath) {\n    return errors.New(\"no key backup present - nothing to export\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure sync tooling includes the data/.siyuan directory - the backup lives there","Export and store the key backup off-workspace immediately after enabling encryption","Watch for NotebookCryptoStateRecoveryRequired states - they precede this failure"],"tags":["crypto","master-password","backup","notebook-encryption","go","siyuan"],"backgroundTag":"missing-backup-file","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}