siyuan-note/siyuan · error
314
314
Error message
Conf.Language(314)
What it means
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.
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
Example fix
// before
err := model.EnsureAssetLocal(absPath) // via ensureReadableAssetLocal
// after
boxID := model.ExtractBoxIDFromAssetsPath(absPath)
if model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID) {
return promptUserToUnlock(boxID)
}
err := model.EnsureAssetLocal(absPath) Defensive patterns
Strategy: try-catch
Validate before calling
boxID := model.ExtractBoxIDFromAssetsPath(absPath)
if boxID != "" && model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID) {
return errors.New(model.Conf.Language(314))
} Try / catch
if err := model.EnsureAssetLocal(absPath); err != nil {
if strings.Contains(err.Error(), model.Conf.Language(314)) {
if uerr := unlockBox(boxID); uerr != nil { return uerr }
return model.EnsureAssetLocal(absPath) // retry once after unlock
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 314
- Query asset failed [ ]
- 377
- Conf.Language(12)
- encrypted asset history is missing valid notebook context
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/a7be4cd01c91fe74.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/asset_download_read.go:131
}
}
if lookupPath == relativePath {
return absPath, nil
}
candidates = append(candidates, absPath)
}
sort.Strings(candidates)
if len(candidates) > 0 {
return candidates[0], nil
}
return "", nil
}
// ensureReadableAssetLocal 先检查加密笔记本准入,再下载原始密文;实际读取仍须认证解密。
func ensureReadableAssetLocal(absPath string) error {
boxID := ExtractBoxIDFromAssetsPath(absPath)
if boxID != "" && IsEncryptedBox(boxID) && !IsBoxUnlocked(boxID) {
return errors.New(Conf.Language(314))
}
if gulu.File.IsSubPath(util.DataDir, absPath) {
if err := EnsureAssetPrefixLocal(absPath); err != nil {
return err
}
}
return EnsureAssetLocal(absPath)
}
// prepareExportAssets 在导出持有笔记本读锁或生成产物之前补齐文档引用的资源。
func prepareExportAssets(boxID string, docPaths []string, includeFootnotes ...bool) error {
if boxID != "" && IsEncryptedBox(boxID) && !IsBoxUnlocked(boxID) {
return errors.New(Conf.Language(314))
}
deferred, err := DeferredSyncAssets()
if err != nil || len(deferred) == 0 {
return err
}View on GitHub (pinned to 9f775e8a12)