siyuan-note/siyuan · error
cannot disable encrypted notebook feature while encrypted no
Error message
cannot disable encrypted notebook feature while encrypted notebooks exist, remove them first
What it means
Thrown by DisableEncryptedNotebook (crypto.go:1077) as a hard precondition guard: the encrypted-notebook feature cannot be turned off while at least one encrypted notebook still exists. Disabling would clear the global MasterSalt/KEKVerifier and delete the key backup, which would permanently lock all remaining encrypted notebooks. The user must remove (or decrypt) every encrypted notebook first.
Source
Thrown at kernel/model/crypto.go:1077
Conf.Save()
IncSync()
return nil
}
// DisableEncryptedNotebook 关闭加密笔记本功能。前置:不能有加密笔记本存在,
// 且不能有依赖当前密钥备份的已删除笔记本历史(否则禁用并删除备份会让这些历史永久锁死,违反 §19)。
// 清除全局加密配置(MasterSalt/KEKVerifier),KEK/DEK 不再可用。
func DisableEncryptedNotebook() error {
notebookCryptoMu.Lock()
defer notebookCryptoMu.Unlock()
// 检查是否还有加密笔记本(含 conf 损坏但存在备份的)
ids, listErr := listAllEncryptedBoxIDs()
if listErr != nil {
return fmt.Errorf("list encrypted notebooks failed: %w", listErr)
}
if len(ids) > 0 {
return errors.New("cannot disable encrypted notebook feature while encrypted notebooks exist, remove them first")
}
// 检查历史目录中是否存在已删除加密笔记本的历史快照:其恢复仍依赖当前 MasterSalt/KEKVerifier,
// 删除备份前必须先清除这些历史(详见设计 §19)
hasHistory, historyErr := scanEncryptedNotebookHistory()
if historyErr != nil {
return fmt.Errorf("check encrypted notebook history failed: %w", historyErr)
}
if hasHistory {
return errors.New(Conf.Language(323))
}
Conf.m.Lock()
Conf.NotebookCrypto.Enabled = false
Conf.NotebookCrypto.MasterSalt = nil
Conf.NotebookCrypto.KEKVerifier = nil
Conf.NotebookCrypto.VerifierNonce = nil
Conf.m.Unlock()
View on GitHub (pinned to 251596fc0d)
Solutions
- Remove every encrypted notebook first (delete or convert to normal), then call DisableEncryptedNotebook again.
- If a damaged notebook cannot be removed through the UI, locate it via listAllEncryptedBoxIDs error context and delete its directory and per-notebook backup manually after confirming the data is expendable.
- Re-run disable only after listAllEncryptedBoxIDs returns an empty set.
Defensive patterns
Strategy: validation
Validate before calling
// Before disable, confirm no encrypted notebooks remain.
ids, err := model.ListAllEncryptedBoxIDs()
if err != nil {
return err
}
if len(ids) > 0 {
return fmt.Errorf("remove encrypted notebooks first: %v", ids)
}
// safe to disable Try / catch
if err := model.DisableEncryptedNotebook(); err != nil {
if strings.Contains(err.Error(), "encrypted notebooks exist, remove them first") {
respond(c, "remove all encrypted notebooks before disabling")
return
}
respond(c, err.Error())
} Prevention
- Remove or decrypt every encrypted notebook before clicking disable.
- Surface the encrypted-notebook count in the disable confirmation UI.
- Handle notebooks whose conf is damaged but that still have backups (they still count).
When it happens
Trigger: POST /api/notebook/disableEncryptedNotebooks when listAllEncryptedBoxIDs returns len(ids) > 0. The scan detects encrypted notebooks via conf.json flags, per-notebook key backups, on-disk ciphertext markers, or runtime identity, so even a notebook whose conf is damaged but has a backup counts.
Common situations: User clicks 'disable encrypted notebooks' in settings before removing their encrypted notebook(s). An encrypted notebook whose conf.json was corrupted but whose backup still marks it encrypted, so it is still counted.
Related errors
- 323
- list encrypted notebooks failed: %w
- check encrypted notebook history failed: %w
- 315
- enable encrypted notebook failed: failed to persist key back
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/75dbdc88657b96d8.
Report an issue: GitHub.