siyuan-note/siyuan · error
Please unlock the encrypted notebook first
Error message
Please unlock the encrypted notebook first
What it means
Reading the current version of a document in an encrypted notebook requires that notebook's data-encryption key (DEK) to be resident in memory. GetDEKIfUnlocked failed - the box is locked - so the kernel returns the localized message 'Please unlock the encrypted notebook first' (Conf.Language(314)) instead of decrypting. The read lock is held during the attempt; without an unlocked DEK the content is inaccessible by design.
Source
Thrown at kernel/model/history_diff.go:326
func readCurrentDocVersionData(blockTree *treenode.BlockTree) (ret []byte, err error) {
relPath, err := filesys.ValidateBoxRelativePath(blockTree.BoxID, blockTree.Path)
if err != nil {
return nil, err
}
encrypted := IsEncryptedBox(blockTree.BoxID)
if encrypted {
HoldBoxReadLock(blockTree.BoxID)
defer ReleaseBoxReadLock(blockTree.BoxID)
}
absPath := filepath.Join(util.DataDir, blockTree.BoxID, filepath.FromSlash(relPath))
ret, err = filelock.ReadFile(absPath)
if err != nil || !encrypted {
return
}
dek, err := GetDEKIfUnlocked(blockTree.BoxID)
if err != nil {
return nil, errors.New(Conf.Language(314))
}
ret, err = DecryptFile(blockTree.BoxID, relPath, dek, ret)
return
}
func loadHistoryDocVersion(historyPath string) (ret *loadedDocVersion, err error) {
absPath, err := validateHistoryPath(historyPath)
if err != nil {
return nil, err
}
if !strings.HasSuffix(strings.ToLower(absPath), ".sy") {
return nil, errors.New("history version is not a document")
}
relPath, err := filepath.Rel(util.HistoryDir, absPath)
if err != nil {
return nil, err
}
parts := strings.SplitN(filepath.ToSlash(relPath), "/", 3)View on GitHub (pinned to afa823b6b4)
Solutions
- Unlock the encrypted notebook in the UI (or via the unlock API), then retry the diff
- Verify you unlocked the box that actually contains the document (box ID from the doc's path)
- For automated runs, complete the unlock step first or keep encrypted boxes out of the flow
Defensive patterns
Strategy: retry
Validate before calling
// kernel/plugin-side: probe unlock state before diffing
if model.IsEncryptedBox(boxID) {
if _,dekErr := model.GetDEKIfUnlocked(boxID); dekErr != nil {
// run the unlock flow, then retry
}
} Try / catch
diff, err := model.DiffDocVersions(left, right)
if err != nil && err.Error() == model.Conf.Language(314) {
// prompt unlock, wait for completion, retry the diff once
} Prevention
- Unlock encrypted boxes at session start before any read APIs are used
- Treat the localized unlock message as a retryable state, not a permanent failure
When it happens
Trigger: Diffing the current version of a document in an encrypted notebook that was not unlocked in this kernel session; unlock expired after kernel restart or relock; a different encrypted box was unlocked than the one containing the doc.
Common situations: Kernel restart clears unlocked keys, then an old UI tab retries a diff; headless scripts calling the API without completing the unlock flow; multiple encrypted boxes where only one was unlocked.
Related errors
- master password migration is pending
- Conf.Language(314)
- initialize encrypted notebook document failed: %w
- Conf.Language(314)
- Conf.Language(314)
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/0e8e8f66d287d910.
Report an issue: GitHub.