siyuan-note/siyuan · error

encrypted notebook locked, please unlock it first

Error message

encrypted notebook locked, please unlock it first

What it means

Returned by mountBox (kernel/model/mount.go:462) when IsEncryptedBox(boxID) is true but IsBoxUnlocked(boxID) is false. Encrypted notebooks hold their DEK only in memory after an explicit UnlockBox call supplies the password; Mount itself takes no password, so it refuses to open a still-locked encrypted notebook.

Source

Thrown at kernel/model/mount.go:462

			CheckUpdate(true)
		}()
	}

	if !gulu.File.IsDir(localPath) {
		return false, errors.New("can not open file, just support open folder only")
	}

	for _, box := range Conf.GetOpenedBoxes() {
		if box.ID == boxID {
			return true, nil
		}
	}

	// 加密笔记本必须先通过 UnlockBox 解出 DEK,否则拒绝挂载。Mount 本身不接收密码,
	// 前端流程为:先调 /api/notebook/unlockBox 解锁,再调 openNotebook 挂载。
	// 使用 IsEncryptedBox 统一判定(含 backup fallback,不依赖 conf 完整性)。
	if IsEncryptedBox(boxID) && !IsBoxUnlocked(boxID) {
		return false, errors.New("encrypted notebook locked, please unlock it first")
	}

	box := &Box{ID: boxID}
	boxConf := box.GetConf()
	boxConf.Closed = false
	if err := box.SaveConf(boxConf); err != nil {
		logging.LogErrorf("save box conf [%s] failed: %s", boxID, err)
	}
	if boxConf.Encrypted {
		markRuntimeEncryptedBox(boxID)
		mountedEncryptedBoxes.Store(boxID, true)
	}
	if _, ensureErr := EnsureBoxDoc(boxID); nil != ensureErr {
		logging.LogErrorf("ensure box document [%s] failed: %s", boxID, ensureErr)
	}

	// 缓存根一级的文档树展开
	files, _, _ := ListDocTree(box.ID, "/", util.SortModeUnassigned, false, false, Conf.FileTree.MaxListCount)

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Call UnlockBox (model.UnlockBox / /api/notebook/unlockBox) with the master password first, then Mount.
  2. If the password is lost, use the backup recovery flow; otherwise the notebook cannot be opened.
  3. Re-unlock after every kernel restart — the unlocked state does not persist.

Example fix

// before
model.Mount(boxID) // encrypted but not unlocked -> error
// after
if err := model.UnlockBox(boxID, password); err != nil {
    return err
}
_, err := model.Mount(boxID)
Defensive patterns

Strategy: validation

Validate before calling

if model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID) {
    if err := model.UnlockBox(boxID, password); err != nil {
        return err
    }
}
return model.Mount(boxID)

Type guard

func needsUnlock(boxID string) bool {
    return model.IsEncryptedBox(boxID) && !model.IsBoxUnlocked(boxID)
}

Prevention

When it happens

Trigger: Calling /api/notebook/openNotebook for an encrypted notebook before calling /api/notebook/unlockBox with the correct password, or after a kernel restart that cleared the in-memory DEK.

Common situations: Kernel was restarted (DEK is volatile), the user cleared the unlock from the UI, or a script orchestrates open without the preceding unlock step.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/e5d14cb98fa1d847. Report an issue: GitHub.