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

  1. Unlock the encrypted notebook in the UI (or via the unlock API), then retry the diff
  2. Verify you unlocked the box that actually contains the document (box ID from the doc's path)
  3. 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

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


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/0e8e8f66d287d910. Report an issue: GitHub.