siyuan-note/siyuan · error

Conf.Language(314)

Error message

Conf.Language(314)

What it means

When reading encrypted document history the kernel needs the notebook's DEK. GetDEKIfUnlocked returns an error when the DEK is unavailable, most commonly because the notebook is locked (not unlocked with its passphrase). The kernel then surfaces Conf.Language(314): 'Please unlock the encrypted notebook first'.

Source

Thrown at kernel/model/history.go:195

	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()
	if err = treenode.CheckSpecJSON(data); nil != err {
		return
	}
	historyTree, err := dataparser.ParseJSONWithoutFix(data, luteEngine.ParseOptions)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Unlock the encrypted notebook (open it / enter passphrase) and retry reading the history
  2. Ensure the notebook is not closed; open it before requesting history content
  3. If unlock fails, verify the access key / passphrase for the notebook
Defensive patterns

Strategy: retry

Validate before calling

const box = (await fetchPost('/api/notebook/lsNotebooks', {})).notebooks.find(n => n.id === boxID);
if (box && box.closed) await fetchPost('/api/notebook/openNotebook', {notebook: boxID}); // triggers unlock flow

Try / catch

try {
  return await fetchPost('/api/history/getDocHistoryContent', {historyPath});
} catch (e) {
  if (String(e.msg).includes('unlock the encrypted notebook')) {
    await promptUnlock(boxID); // user enters passphrase
    return fetchPost('/api/history/getDocHistoryContent', {historyPath}); // retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Opening a history snapshot of an encrypted notebook that is currently locked; accessing history right after app boot before the user unlocked the notebook; DEK cache cleared after re-key operations.

Common situations: Browsing history panel while the encrypted notebook is locked; scripted history export running before unlock; a resumed session where the notebook was re-locked by idle timeout.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/d5136649ac2012c4. Report an issue: GitHub.