{"record":{"id":"a7be4cd01c91fe74","repo":"siyuan-note/siyuan","slug":"314-conf-language-a7be4c","errorCode":"314","errorMessage":"Conf.Language(314)","messagePattern":"Conf\\.Language\\(314\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/asset_download_read.go","lineNumber":131,"sourceCode":"\t\t\t}\n\t\t}\n\t\tif lookupPath == relativePath {\n\t\t\treturn absPath, nil\n\t\t}\n\t\tcandidates = append(candidates, absPath)\n\t}\n\tsort.Strings(candidates)\n\tif len(candidates) > 0 {\n\t\treturn candidates[0], nil\n\t}\n\treturn \"\", nil\n}\n\n// ensureReadableAssetLocal 先检查加密笔记本准入，再下载原始密文；实际读取仍须认证解密。\nfunc ensureReadableAssetLocal(absPath string) error {\n\tboxID := ExtractBoxIDFromAssetsPath(absPath)\n\tif boxID != \"\" && IsEncryptedBox(boxID) && !IsBoxUnlocked(boxID) {\n\t\treturn errors.New(Conf.Language(314))\n\t}\n\tif gulu.File.IsSubPath(util.DataDir, absPath) {\n\t\tif err := EnsureAssetPrefixLocal(absPath); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn EnsureAssetLocal(absPath)\n}\n\n// prepareExportAssets 在导出持有笔记本读锁或生成产物之前补齐文档引用的资源。\nfunc prepareExportAssets(boxID string, docPaths []string, includeFootnotes ...bool) error {\n\tif boxID != \"\" && IsEncryptedBox(boxID) && !IsBoxUnlocked(boxID) {\n\t\treturn errors.New(Conf.Language(314))\n\t}\n\tdeferred, err := DeferredSyncAssets()\n\tif err != nil || len(deferred) == 0 {\n\t\treturn err\n\t}","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/asset_download_read.go#L113-L149","documentation":"ensureReadableAssetLocal enforces that an asset belonging to an encrypted notebook can only be made locally readable when that notebook is unlocked; otherwise it returns i18n message 314 ('Please unlock the encrypted notebook first'). The check runs before downloading the raw ciphertext (EnsureAssetPrefixLocal); actual read still requires authenticated decryption afterwards.","triggerScenarios":"Called by readAssetBytesInBox, uploadAssets2Cloud, RenameAsset, and PrepareRichClipboardAssets when the target asset's box (ExtractBoxIDFromAssetsPath) is an encrypted box and IsBoxUnlocked returns false — e.g. after kernel restart or manual lock.","commonSituations":"Copying rich-text content containing encrypted-notebook assets to the clipboard; renaming or re-uploading assets in a locked encrypted notebook; background jobs operating on encrypted boxes without the user having unlocked them.","solutions":["Unlock the encrypted notebook (enter passphrase) and retry the operation","In scripts/plugins, call the box unlock API and confirm IsBoxUnlocked(boxID) before these asset operations","Reorder automation so unlocking happens first in the workflow","Check ExtractBoxIDFromAssetsPath output to confirm the asset really belongs to the encrypted box you think it does"],"exampleFix":"// before\nerr := model.EnsureAssetLocal(absPath) // via ensureReadableAssetLocal\n// after\nboxID := model.ExtractBoxIDFromAssetsPath(absPath)\nif model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID) {\n    return promptUserToUnlock(boxID)\n}\nerr := model.EnsureAssetLocal(absPath)","handlingStrategy":"try-catch","validationCode":"boxID := model.ExtractBoxIDFromAssetsPath(absPath)\nif boxID != \"\" && model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID) {\n    return errors.New(model.Conf.Language(314))\n}","typeGuard":null,"tryCatchPattern":"if err := model.EnsureAssetLocal(absPath); err != nil {\n    if strings.Contains(err.Error(), model.Conf.Language(314)) {\n        if uerr := unlockBox(boxID); uerr != nil { return uerr }\n        return model.EnsureAssetLocal(absPath) // retry once after unlock\n    }\n    return err\n}","preventionTips":["Unlock encrypted notebooks before clipboard/upload/rename operations on their assets","Add IsBoxUnlocked pre-checks in plugins and scripts","Re-unlock after every kernel restart before background asset jobs","Map asset paths to their owning box early to fail fast with a clear prompt"],"tags":["encrypted-notebook","lock","assets","access-control","i18n"],"backgroundTag":"authentication-required","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"}