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 decErr

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Unlock the notebook first via the unlock API/UI with the correct access key, then retry the copy.
  2. Surface the localized message (Language(314)) to the user and prompt for the access key.
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/5664629b01f1e3f8. Report an issue: GitHub.