siyuan-note/siyuan · warning
%s
Error message
%s
What it means
The error message is the localized string from model.Conf.Language(314), returned when the encrypted notebook owning the asset is locked: model.IsBoxUnlocked(boxID) is false. The kernel refuses to decrypt and copy an asset from a notebook the user has not unlocked, since the plaintext would be derivable without authorization.
Source
Thrown at kernel/api/file.go:63
// 对加密笔记本的任何文件读写都应拒绝——合法读写走专用 API(upload/getBlockKramdown 等,已加密感知),
// 避免密文泄漏给插件或明文破坏加密格式。
// 防止 symlink 绕过:找到最长已存在的父路径,解析 symlink 后拼回剩余路径,再检查是否落入加密 box。
func rejectEncryptedBoxPath(absPath string) bool {
return model.EncryptedRawPathBoxID(absPath) != ""
}
// copyDecryptedAsset 将加密 asset 解密后复制到目标路径(dest 必须在工作区外)。
func copyDecryptedAsset(src, dest string) error {
// 安全守卫:dest 必须在工作区外,防止解密后的明文落入工作区普通目录
if gulu.File.IsSubPath(util.WorkspaceDir, dest) {
return fmt.Errorf("refuse to write decrypted asset inside workspace")
}
boxID := model.ExtractBoxIDFromAssetsPath(src)
if boxID == "" || !model.IsEncryptedBox(boxID) {
return fmt.Errorf("source is not an encrypted asset")
}
if !model.IsBoxUnlocked(boxID) {
return fmt.Errorf("%s", model.Conf.Language(314))
}
if err := model.EnsureAssetLocal(src); err != nil {
return err
}
model.HoldBoxReadLock(boxID)
defer model.ReleaseBoxReadLock(boxID)
dek, dekErr := model.GetDEKIfUnlocked(boxID)
if dekErr != nil {
return dekErr
}
diskName := filepath.Base(src)
data, readErr := os.ReadFile(src)
if readErr != nil {
return readErr
}
plain, decErr := model.DecryptAsset(boxID, diskName, dek, data)
if decErr != nil {
return decErrView on GitHub (pinned to 8641553a1f)
Solutions
- Unlock the notebook first via the unlock API/UI with the correct access key, then retry the copy.
- Surface the localized message (Language(314)) to the user and prompt for the access key.
- Re-unlock after kernel restart since unlock state is not persisted.
Example fix
// before copyFile(src, dest) // box still locked // after unlockBox(boxID, accessKey) // user supplies key copyFile(src, dest)
Defensive patterns
Strategy: try-catch
Try / catch
try { await copyFile(src, dest) } catch (e) { if (isBoxLockedMessage(e)) { await promptUnlockBox(boxID); await copyFile(src, dest) } else { throw e } } Prevention
- Unlock encrypted notebooks before scripted asset operations
- Re-unlock after kernel restart; unlock state is not persistent
- Surface the localized locked-box message to prompt for the access key
When it happens
Trigger: Calling copyFile on an asset of an encrypted notebook whose access key has not been provided (or whose unlock session expired) before the copy is requested.
Common situations: App restart clears in-memory unlock state; user copied the asset link before locking the box; automated scripts operate on locked notebooks without unlocking first; wrong access key entered earlier left the box locked.
Related errors
- Conf.Language(314)
- Conf.Language(314)
- encrypted notebook is not accessible
- no DEK cached for box
- Conf.Language(314)
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/5664629b01f1e3f8.
Report an issue: GitHub.