siyuan-note/siyuan · error
cannot change master password while encrypted notebooks are
Error message
cannot change master password while encrypted notebooks are unlocked (DEKs in memory), lock them first
What it means
Thrown by ChangeMasterPassword when cachedDEKs is non-empty, meaning at least one encrypted notebook is currently unlocked (its DEK is in memory). Changing the master password re-wraps all WrappedDEKs with a new KEK; if a notebook is unlocked, its cached DEK would become inconsistent with the on-disk WrappedDEK after the KEK switch. This guard prevents that inconsistency.
Source
Thrown at kernel/model/crypto.go:1696
// Phase 2: 切换全局 verifier
// Phase 3: 写入各 box conf + backup
// Phase 4: 清除 manifest
//
// 注意:必须在所有加密笔记本都已 Unmount 的状态下调用(DEK 不在内存),否则新旧 KEK 切换会让缓存与磁盘不一致。
func ChangeMasterPassword(oldPassword, newPassword string) error {
if len(newPassword) == 0 {
return errors.New("new password must not be empty")
}
notebookCryptoMu.Lock()
defer notebookCryptoMu.Unlock()
// 改密期间不能有已 Mount 的加密笔记本(DEK 在内存),否则新旧 KEK 切换会让缓存与磁盘不一致
cachedDEKsLock.RLock()
dekCount := len(cachedDEKs)
cachedDEKsLock.RUnlock()
if dekCount > 0 {
return errors.New("cannot change master password while encrypted notebooks are unlocked (DEKs in memory), lock them first")
}
oldKEK, err := deriveKEK(oldPassword)
if err != nil {
return err
}
defer zeroAndClear(oldKEK)
Conf.m.Lock()
nc := Conf.NotebookCrypto
Conf.m.Unlock()
params, validErr := util.ValidateArgon2Params(nc.KDFParams)
if validErr != nil {
return validErr
}
newKEK := util.DeriveKey(newPassword, nc.MasterSalt, params)
defer zeroAndClear(newKEK)View on GitHub (pinned to 251596fc0d)
Solutions
- Lock all encrypted notebooks first (call LockBox or use the UI lock action), then retry ChangeMasterPassword.
- If using the API, call getEncryptedNotebookStatus to verify all encrypted notebooks show a locked state before calling changeMasterPassword.
- Reduce AutoLockMinutes so notebooks auto-lock sooner, reducing the window where this guard blocks password changes.
Defensive patterns
Strategy: validation
Validate before calling
// Check that no encrypted notebooks are unlocked before changing password: // (This mirrors the internal check in ChangeMasterPassword) // At the API level, call getEncryptedNotebookStatus first and verify // all boxes show locked state. There is no public 'lock all' API, so // the user must lock each notebook individually via the UI or API.
Prevention
- Lock all encrypted notebooks before initiating a password change.
- Use getEncryptedNotebookStatus to verify all boxes are locked before calling changeMasterPassword.
- Design the UI to auto-lock all notebooks as a pre-step of the password-change flow.
When it happens
Trigger: Called via changeMasterPassword API while one or more encrypted notebooks are in the Unlocked state (DEK cached in memory). The check acquires cachedDEKsLock.RLock, reads len(cachedDEKs), and rejects if > 0.
Common situations: User unlocks a notebook, then tries to change the master password without locking it first. Auto-lock hasn't triggered yet. A second device synced new notebooks that are auto-unlocked on startup. User forgot to lock after a session.
Related errors
- Decryption failed: incorrect key or corrupted data [box=%s]
- Master password change partially failed. Please restart SiYu
- path belongs to encrypted notebook [%s]: %s
- unsupported encrypted notebook key envelope
- encrypted notebook key envelope creation time is missing
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/c0cab84244bd478f.
Report an issue: GitHub.