siyuan-note/siyuan · error

Please unlock the encrypted notebook first

Error message

Please unlock the encrypted notebook first

What it means

Returned by copyAssetDecryptIfEncrypted when GetDEKIfUnlocked fails for the source box — the encrypted notebook is locked. The function is fail-closed: it refuses to copy rather than copying ciphertext (which would produce an unusable file at the destination). The message comes from Conf.Language(314): 'Please unlock the encrypted notebook first'.

Source

Thrown at kernel/model/crypto.go:2513

	return &crypt, nil
}

// copyAssetDecryptIfEncrypted 把 srcPath 的 asset 复制到 destPath。
// 若 srcPath 在已解锁的加密笔记本下,读密文→解密→写明文到 destPath(导出目录);
// 否则走 filelock.Copy 原路径(字节级复制,密文/明文均可)。
func copyAssetDecryptIfEncrypted(srcPath, destPath string) error {
	if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
		return err
	}

	boxID := ExtractBoxIDFromAssetsPath(srcPath)
	if boxID != "" && IsEncryptedBox(boxID) {
		HoldBoxReadLock(boxID)
		defer ReleaseBoxReadLock(boxID)
		dek, err := GetDEKIfUnlocked(boxID)
		if err != nil {
			// 加密笔记本未解锁:fail-closed,拒绝复制(不复制密文,避免泄漏无效文件)
			return errors.New(Conf.Language(314))
		}
		raw, readErr := filelock.ReadFile(srcPath)
		if readErr != nil {
			return readErr
		}
		diskName := filepath.Base(srcPath)
		plain, decErr := DecryptAsset(boxID, diskName, dek, raw)
		if decErr != nil {
			return errors.New(Conf.Language(316))
		}
		if err := filelock.WriteFile(destPath, plain); err != nil {
			return err
		}
		return nil
	}
	return filelock.Copy(srcPath, destPath)
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Unlock the encrypted notebook (UnlockBox with the user's password) and retry the copy/export.
  2. Surface a clear UI prompt telling the user which notebook needs unlocking, rather than a generic copy failure.
  3. For background jobs, check isBoxUnlockedForAccess(boxID) before scheduling asset operations and defer or queue the job if locked.
Defensive patterns

Strategy: try-catch

Validate before calling

// Check unlock state before triggering asset copy
if model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID) {
    return fmt.Errorf("please unlock notebook %s first", boxID)
}

Try / catch

err := copyAssetDecryptIfEncrypted(src, dst)
if err != nil {
    msg := err.Error()
    if strings.Contains(msg, "unlock") {
        // surface unlock prompt to user, retry after unlock
    }
    return err
}

Prevention

When it happens

Trigger: An asset copy/export operation (e.g., export to temp, publish, or asset serving) hits copyAssetDecryptIfEncrypted while the source notebook's DEK is not in memory. Triggered during export, thumbnail generation, or asset serving for a locked encrypted notebook.

Common situations: User attempts to export or preview an asset from an encrypted notebook that auto-locked. Publish/sync background job runs after the auto-lock timer fired. Fresh app start where the notebook wasn't unlocked.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/45a63e525e292715. Report an issue: GitHub.