siyuan-note/siyuan · warning
312
312
Error message
Encrypted notebook feature is already enabled
What it means
EnableEncryptedNotebook returns Conf.Language(312) ('encrypted notebook feature is already enabled') when the current NotebookCrypto is both Enabled and structurally complete (notebookCryptoConfigurationComplete). The guard prevents a second enable from generating a fresh MasterSalt that would orphan existing WrappedDEKs. This fires before the hasEncrypted/hasHistory/hasBackup recovery path.
Source
Thrown at kernel/model/crypto.go:986
return EnableEncryptedNotebook(password)
}
// EnableEncryptedNotebook 启用加密笔记本功能:生成 MasterSalt、派生 KEK、写入校验值并持久化。
// 重复调用(已启用)返回错误,避免覆盖现有加密笔记本的密钥参数。
// KEK 不缓存——启用后用户需对每个加密笔记本单独调 UnlockBox 解锁。
func EnableEncryptedNotebook(password string) error {
if len(password) == 0 {
return errors.New("password must not be empty")
}
notebookCryptoMu.Lock()
defer notebookCryptoMu.Unlock()
Conf.m.RLock()
current := *Conf.NotebookCrypto
Conf.m.RUnlock()
if current.Enabled && notebookCryptoConfigurationComplete(¤t) {
return errors.New(Conf.Language(312))
}
hasEncrypted, listErr := hasEncryptedNotebook()
if listErr != nil {
return fmt.Errorf("list encrypted notebooks failed: %w", listErr)
}
hasHistory, historyErr := scanEncryptedNotebookHistory()
if historyErr != nil {
return fmt.Errorf("check encrypted notebook history failed: %w", historyErr)
}
hasBackup := filelock.IsExist(dataCryptoBackupPath())
if hasEncrypted || hasHistory || hasBackup {
// 现存笔记本、已删除笔记本历史或全局备份均表示已有密钥域,必须恢复并认证,不能生成新 MasterSalt。
kek, restoreErr := tryRestoreNotebookCryptoFromBackupLocked(password)
if kek != nil {
zeroAndClear(kek)
}
if restoreErr != nil {View on GitHub (pinned to 251596fc0d)
Solutions
- Check NotebookCryptoEnabled() before offering the enable action in the UI.
- If the intent is to change the master password, use the change-password flow instead of re-enabling.
- If the configuration is corrupt-but-enabled, address the corruption directly rather than re-enabling.
Example fix
// before
err := model.EnableEncryptedNotebook(password)
// after
if model.NotebookCryptoEnabled() {
// already enabled; route to change-password or unlock instead
return
}
err := model.EnableEncryptedNotebook(password) Defensive patterns
Strategy: validation
Validate before calling
// Check enabled state before offering the enable action.
if model.NotebookCryptoEnabled() {
// route to unlock / change-password instead of enable
} Prevention
- Gate the 'enable encryption' UI on NotebookCryptoEnabled() returning false.
- If the user wants to change credentials, route to the change-master-password flow, not re-enable.
- Reflect enabled state in UI immediately after enable succeeds to prevent duplicate submissions.
When it happens
Trigger: EnableEncryptedNotebook (or EnableEncryptedNotebookWithSync falling through to it) is called while Conf.NotebookCrypto.Enabled is true and all required fields are present. Typical when the user clicks 'enable' on a device where encryption is already active.
Common situations: User double-invokes enable; UI state desync showing enable as available when it is already on; race where two enable requests are submitted; restored conf.json from a device where encryption was already enabled.
Related errors
- Cannot import a key backup while encrypted notebooks are ena
- encrypted notebook key material is missing
- Encrypted notebooks already exist but the master key backup
- Decryption failed: incorrect key or corrupted data
- password must not be empty
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/d0d0b5f2f171867f.
Report an issue: GitHub.