siyuan-note/siyuan · error
Please unlock the encrypted notebook first
Error message
Please unlock the encrypted notebook first
What it means
Thrown by GetDocHistoryContent when reading the content of an encrypted document's history snapshot. The history path is parsed to extract the notebook ID (histBoxID), which is confirmed to be encrypted (IsEncryptedBox). The function then acquires a read lock on the box and calls GetDEKIfUnlocked(histBoxID) to obtain the Data Encryption Key. If GetDEKIfUnlocked returns an error — meaning the notebook is encrypted but not currently unlocked (no DEK in memory) — the function returns Conf.Language(314) = "Please unlock the encrypted notebook first". The history content cannot be decrypted without the DEK.
Source
Thrown at kernel/model/history.go:194
relPath := strings.TrimPrefix(filepath.ToSlash(historyPath), filepath.ToSlash(util.HistoryDir))
relPath = strings.TrimPrefix(relPath, "/")
pathParts := strings.SplitN(relPath, "/", 3)
ciphertext := util.IsCiphertext(data)
if ciphertext {
if len(pathParts) < 3 || !ast.IsNodeIDPattern(pathParts[1]) {
err = errors.New("encrypted document history is missing notebook context")
return
}
histBoxID := pathParts[1]
if !IsEncryptedBox(histBoxID) {
err = fmt.Errorf("encrypted document history has no matching notebook [%s]", histBoxID)
return
}
HoldBoxReadLock(histBoxID)
defer ReleaseBoxReadLock(histBoxID)
dek, dekErr := GetDEKIfUnlocked(histBoxID)
if dekErr != nil {
err = errors.New(Conf.Language(314))
return
}
data, err = DecryptFile(histBoxID, pathParts[2], dek, data)
if err != nil {
logging.LogErrorf("decrypt history [%s] failed: %s", historyPath, err)
return
}
} else if len(pathParts) >= 2 && IsEncryptedBox(pathParts[1]) {
err = fmt.Errorf("encrypted notebook document history is plaintext [%s]", pathParts[1])
return
}
isLargeDoc = 1024*1024*1 <= len(data)
luteEngine := NewLute()
historyTree, err := dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)
if err != nil {
logging.LogErrorf("parse tree from file [%s] failed: %s", historyPath, err)
returnView on GitHub (pinned to 251596fc0d)
Solutions
- Unlock the encrypted notebook first (enter the notebook password via the UI or the unlock API), then retry viewing the history content.
- If the kernel was restarted, re-unlock the notebook — DEKs are not persisted across kernel restarts.
- As an API client, call the notebook unlock endpoint with the correct password before requesting history content from encrypted notebooks.
Example fix
// before
const history = await post('/api/history/getDocHistoryContent', { historyPath: encryptedDocHistoryPath })
// after — unlock the encrypted notebook first
await post('/api/notebook/openNotebook', { notebook: encryptedBoxId, password: notebookPassword })
const history = await post('/api/history/getDocHistoryContent', { historyPath: encryptedDocHistoryPath }) Defensive patterns
Strategy: validation
Validate before calling
// Unlock the encrypted notebook before reading its history content
if (isEncryptedNotebook(histBoxID) && !isNotebookUnlocked(histBoxID)) {
await post('/api/notebook/unlockNotebook', { notebook: histBoxID, password })
}
const history = await post('/api/history/getDocHistoryContent', { historyPath }) Try / catch
// Catch the unlock-required error and prompt for password
try {
const history = await post('/api/history/getDocHistoryContent', { historyPath })
} catch (e) {
if (e.message.includes('unlock the encrypted notebook')) {
const password = await promptForPassword()
await post('/api/notebook/unlockNotebook', { notebook: histBoxID, password })
const history = await post('/api/history/getDocHistoryContent', { historyPath })
} else {
throw e
}
} Prevention
- Unlock encrypted notebooks at the start of each session before accessing their history.
- After a kernel restart, re-enter passwords for all encrypted notebooks you need to access.
- Track unlock state in the UI and prompt for password before history operations on encrypted notebooks.
When it happens
Trigger: Calling the history content API (POST /api/history/getDocHistoryContent) for a history snapshot that belongs to an encrypted notebook, while that notebook is currently locked (the user has not entered the password to unlock it). The encrypted history .sy file is ciphertext and cannot be parsed without decrypting it with the DEK.
Common situations: User opens the history panel for a document in an encrypted notebook that is currently locked; the kernel was restarted (DEKs are session-only) and the user tries to view history before re-entering the notebook password; an API client tries to read encrypted history without first unlocking the notebook.
Related errors
- encrypted notebook is locked, please unlock it first
- attribute view history context is ambiguous [%s]
- check encrypted notebook history failed: %w
- 323
- encrypted notebook is locked, please unlock it first
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/9f5a159ee9c24529.
Report an issue: GitHub.