{"record":{"id":"196b84196257998b","repo":"siyuan-note/siyuan","slug":"conf-language-391","errorCode":null,"errorMessage":"Conf.Language(391)","messagePattern":"Conf\\.Language\\(391\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":1755,"sourceCode":"\t\t}\n\t}\n\n\t// 检查路径深度是否超过限制\n\tfor _, fromPath := range fromPaths {\n\t\tfromBox := pathsBoxes[fromPath]\n\t\tchildDepth := util.GetChildDocDepth(filepath.Join(util.DataDir, fromBox.ID, fromPath))\n\t\tif depth := strings.Count(toPath, \"/\") + childDepth; 6 < depth && !Conf.FileTree.AllowCreateDeeper {\n\t\t\terr = errors.New(Conf.Language(118))\n\t\t\treturn\n\t\t}\n\t}\n\n\t// 禁止跨加密边界移动文档：加密笔记本是孤岛，不同加密笔记本各有独立 DEK，\n\t// 跨边界移动（普通↔加密、加密 A↔加密 B）会导致密文用错 DEK 损坏数据\n\tfor _, fromPath := range fromPaths {\n\t\tfromBox := pathsBoxes[fromPath]\n\t\tif fromBox.ID != toBox.ID && !IsSameCryptoBoundary(fromBox.ID, toBox.ID) {\n\t\t\terr = errors.New(Conf.Language(391))\n\t\t\treturn\n\t\t}\n\t}\n\n\t// A progress layer appears when moving more than 64 documents at once https://github.com/siyuan-note/siyuan/issues/9356\n\tsubDocsCount := 0\n\tfor _, fromPath := range fromPaths {\n\t\tfromBox := pathsBoxes[fromPath]\n\t\tsubDocsCount += countSubDocs(fromBox.ID, fromPath)\n\t}\n\tneedShowProgress := 64 < subDocsCount\n\tif needShowProgress {\n\t\tdefer util.PushClearProgress()\n\t}\n\n\tFlushTxQueue()\n\tluteEngine := util.NewLute()\n\trefresh := newMoveDocsRefresh()","sourceCodeStart":1737,"sourceCodeEnd":1773,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/file.go#L1737-L1773","documentation":"MoveDocs forbids moving documents across the encryption boundary of encrypted notebooks (key 391). Encrypted notebooks each use their own data encryption key (DEK); relocating ciphertext into another notebook would encrypt/decrypt with the wrong key and corrupt data. Only moves within one notebook or between boundaries that IsSameCryptoBoundary allows are permitted.","triggerScenarios":"moveDocs where fromBox.ID != toBox.ID and IsSameCryptoBoundary(fromBox.ID, toBox.ID) is false — e.g. plain notebook to encrypted notebook, encrypted A to encrypted B, or encrypted to plain.","commonSituations":"Bulk reorganization scripts that ignore notebook encryption status; sync setups mixing encrypted and plain notebooks; users assuming encrypted notebooks behave like normal ones.","solutions":["Move the documents only within the same notebook or to a notebook with the same encryption boundary","Create the document in the target notebook and re-enter the content manually (content is re-encrypted at creation)","Temporarily export/import instead of a raw move"],"exampleFix":"// before\nawait fetchPost('/api/filetree/moveDocs', {fromPaths: ['/doc.sy'], toNotebook: encryptedBoxID, toPath: '/'})\n// after\nconst srcEnc = isEncrypted(srcBoxID), dstEnc = isEncrypted(toBoxID)\nif (srcEnc === dstEnc) { await fetchPost('/api/filetree/moveDocs', {fromPaths: ['/doc.sy'], toNotebook: toBoxID, toPath: '/'}) } else { /* recreate content in target */ }","handlingStrategy":"validation","validationCode":"if (srcBoxID !== toBoxID && !sameCryptoBoundary(srcBoxID, toBoxID)) throw new Error('Cross-encrypted-notebook move not allowed')","typeGuard":null,"tryCatchPattern":"try { await fetchPost('/api/filetree/moveDocs', args) } catch (e) { if (String(e).includes('encrypted notebook')) { /* recreate content in target instead */ } }","preventionTips":["Track which notebooks are encrypted and never mix them in move batches","Recreate content in the target notebook when a boundary move is needed","Never attempt raw file copies between encrypted and plain notebooks"],"tags":["encryption","move","data-integrity"],"backgroundTag":"unsupported-operation","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"}