siyuan-note/siyuan · error
encrypted notebook is locked, please unlock it first
Error message
encrypted notebook is locked, please unlock it first
What it means
Thrown by holdAVBoxReadLock when an Attribute View belongs to an encrypted notebook whose DEK is no longer cached (the box was locked, or the auto-lock timer fired). The lock was acquired but AVIsBoxUnlocked then returned false, so reading the AV — which requires the DEK to decrypt — is refused to avoid returning ciphertext or stale plaintext.
Source
Thrown at kernel/av/encrypted_hook.go:58
// AVIsBoxUnlocked 由 model 层注入,判断加密笔记本是否仍持有 DEK。
var AVIsBoxUnlocked func(boxID string) bool
// AVGetBlockBoxID 由 model 层注入,返回 blockID 所在的 boxID(查 blocktree)。
// 用于镜像写入时校验源块与 AV 定义是否处于同一加密边界。
var AVGetBlockBoxID func(blockID string) string
func holdAVBoxReadLock(boxID string) (release func(), err error) {
release = func() {}
if boxID == "" || AVIsEncryptedBox == nil || !AVIsEncryptedBox(boxID) {
return
}
if AVLockAcquire == nil || AVLockRelease == nil || AVIsBoxUnlocked == nil {
return nil, errors.New("encrypted notebook lock callbacks are not initialized")
}
AVLockAcquire(boxID)
if !AVIsBoxUnlocked(boxID) {
AVLockRelease(boxID)
return nil, errors.New("encrypted notebook is locked, please unlock it first")
}
return func() {
AVLockRelease(boxID)
}, nil
}
// pendingAVBox 记录首次创建的 AV 归属哪个加密 box。
// handler 层创建 AV 前调 SetAVBoxID(avID, boxID),SaveAttributeView 时
// findAttributeViewPath 会先查 pending 映射,找到则写入对应加密笔记本路径。
var pendingAVBox = map[string]string{}
var pendingAVBoxLock = sync.RWMutex{}
// SetAVBoxID 预设 AV 定义的归属 box。加密笔记本创建 AV 时调用,boxID 为空时清理映射。
// 普通笔记本不需要调(AV 默认走全局路径)。
func SetAVBoxID(avID, boxID string) {
pendingAVBoxLock.Lock()
defer pendingAVBoxLock.Unlock()
if boxID != "" {View on GitHub (pinned to 251596fc0d)
Solutions
- Unlock the notebook in the UI, then retry the operation (the kernel re-caches the DEK).
- Raise or disable the auto-lock timeout if this recurs during long exports.
- Avoid concurrent lock-and-read: pause sync/export before locking a notebook that owns AVs.
Defensive patterns
Strategy: validation
Validate before calling
// Before performing an AV read on a possibly-encrypted box, check unlock state.
func canReadAV(boxID string) bool {
if av.AVIsEncryptedBox == nil || !av.AVIsEncryptedBox(boxID) {
return true // plain notebook
}
if av.AVIsBoxUnlocked == nil {
return false // encrypted but callbacks not ready
}
return av.AVIsBoxUnlocked(boxID)
} Try / catch
// Treat a lock error as a recoverable precondition failure.
release, err := av.HoldAVBoxReadLock(boxID) // exported wrapper if available
if err != nil {
// prompt the user to unlock, then retry the original operation
return err
}
defer release() Prevention
- Check av.AVIsBoxUnlocked(boxID) before any operation that touches an encrypted notebook's AV.
- Do not run long exports against encrypted AVs while auto-lock is enabled.
- Pause sync before manually locking a notebook that owns databases.
When it happens
Trigger: Any AV read on an encrypted box races with a lock event. Concrete call sites: parseAttributeViewByPathInBox (av.go:788) and loadAttributeViewSearchInfoInBox (av.go:631), both invoked while the editor, search index, or sync is touching an AV inside a notebook the user just locked.
Common situations: User clicks 'Lock notebook' (or auto-lock trips) while a database view is open or being indexed; sync triggers re-encryption mid-read; long-running export touches encrypted AVs after the session expired.
Related errors
- 239
- encrypted notebook is locked, please unlock it first
- path belongs to encrypted notebook [%s]: %s
- CLI does not support encrypted notebook [%s]
- CLI does not support files in encrypted notebooks
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/4cd006a0eb668e78.
Report an issue: GitHub.