siyuan-note/siyuan · warning
cannot disable encrypted notebook feature while encrypted…
Error message
cannot disable encrypted notebook feature while encrypted notebooks exist, remove them first
What it means
DisableEncryptedNotebook refuses to turn the feature off while any encrypted notebook still exists: "cannot disable encrypted notebook feature while encrypted notebooks exist, remove them first". Disabling clears MasterSalt/KEKVerifier and deletes the key backup, which would permanently lock any remaining encrypted notebooks, so the guard is a deliberate data-safety precondition, not a fault.
Solutions
- Remove all encrypted notebooks first (delete them in the UI or delete their directories, emptying dependent history), then call DisableEncryptedNotebook again
- Decrypt/copy the notebook contents out to ordinary notebooks before removal if the data must be kept, since keys are discarded on disable
- If a leftover encrypted notebook is already deleted from the UI, ensure its directory is gone from the workspace data folder so enumeration no longer sees it
Example fix
// before
model.DisableEncryptedNotebook() // fails while encrypted notebooks exist
// after
for _, boxID := range remainingEncryptedBoxIDs() {
model.RemoveBox(boxID) // or export/decrypt contents first
}
err := model.DisableEncryptedNotebook() // now succeeds Defensive patterns
Strategy: validation
Validate before calling
// Guard before disabling: no encrypted notebooks may remain
ids, err := listAllEncryptedBoxIDs()
if err == nil && len(ids) > 0 { /* remove/export these notebooks first */ } Type guard
func isEncryptedNotebooksRemain(err error) bool {
return err != nil && strings.Contains(err.Error(), "while encrypted notebooks exist")
} Try / catch
if err := model.DisableEncryptedNotebook(); err != nil {
if isEncryptedNotebooksRemain(err) { /* enumerate and delete/decrypt remaining notebooks, then retry */ }
} Prevention
- Before disabling, list encrypted notebooks and export/decrypt any data you need — disabling discards the keys permanently
- Also clear encrypted-notebook history (deleted-notebook snapshots) or the disable will fail on the next guard
- Keep test notebooks unencrypted or remove them after experiments
- Automate the remove-then-disable sequence in scripts instead of calling disable unconditionally
When it happens
Trigger: Calling DisableEncryptedNotebook when listAllEncryptedBoxIDs returns at least one notebook whose conf.BoxConf.Encrypted is true — including notebooks whose main conf is damaged but that still carry the encrypted flag/backup.
Common situations: User tries to disable encryption via API/script while encrypted notebooks remain; leftover encrypted test notebooks in the workspace; forgotten notebooks in the trash/other locations still flagged encrypted.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- cannot change master password while encrypted notebooks are…
- encrypted box db not opened for box
- encrypted box db not opened for box
- no encrypted key material for box
- 26
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/75dbdc88657b96d8.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/crypto.go:1099
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 9f775e8a12)