siyuan-note/siyuan · error
encrypted notebook history has no valid key material
Error message
encrypted notebook history has no valid key material
What it means
readEncryptedHistoryBoxEncryptionCandidates collects candidate key material from every encrypted history box dir. If the scan succeeded but produced zero candidates and zero candidate errors, it returns 'encrypted notebook history has no valid key material'. This distinguishes 'history exists but none of its boxes is encrypted / carries backup material' from a scan failure, so the KEK verification caller knows it cannot verify against anything.
Source
Thrown at kernel/model/crypto.go:917
boxConf := conf.NewBoxConf()
if readErr = gulu.JSON.UnmarshalJSON(data, boxConf); readErr != nil {
candidateErrors = append(candidateErrors, readErr)
} else if boxConf.Encrypted && boxConf.BoxCrypt != nil {
if readErr = validateBoxEncryption(boxConf.BoxCrypt); readErr != nil {
candidateErrors = append(candidateErrors, readErr)
} else {
ret = append(ret, boxConf.BoxCrypt)
}
}
}
}
if len(ret) > 0 {
return ret, nil
}
if len(candidateErrors) > 0 {
return nil, errors.Join(candidateErrors...)
}
return nil, errors.New("encrypted notebook history has no valid key material")
}
// HasEncryptedNotebookHistory 在扫描失败时按存在依赖处理,避免调用方因 I/O 或权限错误删除恢复材料。
func HasEncryptedNotebookHistory() bool {
hasHistory, err := scanEncryptedNotebookHistory()
if err != nil {
logging.LogErrorf("scan encrypted notebook history failed: %s", err)
return true
}
return hasHistory
}
// isEncryptedHistoryBoxDir 判断历史目录中的 boxID 子目录是否属于加密笔记本。
// 优先看 notebook-crypto-backup.json(删除前随 box 目录整体备份,是加密身份的权威标识),
// 再 fallback 到 conf.json 的 Encrypted 标志。
func isEncryptedHistoryBoxDir(boxDir string) (bool, error) {
siyuanDir := filepath.Join(boxDir, ".siyuan")
backupPath := filepath.Join(siyuanDir, "notebook-crypto-backup.json")View on GitHub (pinned to 8641553a1f)
Solutions
- Confirm you are operating on the correct workspace: check data/history for snapshots containing .siyuan/notebook-crypto-backup.json
- If history was pruned, restore snapshots from backup/sync before running KEK verification
- If no encrypted notebook ever existed in this workspace, skip KEK verification — there is nothing to verify against
- Check that snapshot box subdirectories were not renamed (they must match IsNodeIDPattern) by a previous restore
Example fix
// before: wrong workspace
verifyKEKAgainstEncryptedHistory("/tmp/other-workspace")
// after: point at the workspace that holds encrypted history
verifyKEKAgainstEncryptedHistory("~/SiYuan") // contains data/history/<ts>-<delete>/<boxID>/.siyuan/notebook-crypto-backup.json Defensive patterns
Strategy: fallback
Validate before calling
hasEnc := false
for _, d := range historySnapshotDirs() {
if _, err := os.Stat(filepath.Join(d, ".siyuan", "notebook-crypto-backup.json")); err == nil { hasEnc = true; break }
}
if !hasEnc { /* no encrypted history — skip KEK verification */ } Type guard
func hasEncryptedHistoryMaterial(historyDir string) bool {
entries, err := os.ReadDir(historyDir); if err != nil { return false }
for _, e := range entries {
p := filepath.Join(historyDir, e.Name(), ".siyuan", "notebook-crypto-backup.json")
if _, err := os.Stat(p); err == nil { return true }
}
return false
} Try / catch
cands, err := readEncryptedHistoryBoxEncryptionCandidates()
if err != nil && strings.Contains(err.Error(), "no valid key material") {
// this workspace has no encrypted history: verification is a no-op, not a failure of the KEK
return nil
} Prevention
- Point migration/verification at the workspace that actually holds encrypted history
- Disable history pruning of encrypted snapshots until migration completes
- Back up data/history before major restores
- Verify encrypted notebooks exist (conf.json Encrypted=true or backup markers) before expecting key material
When it happens
Trigger: verifyKEKAgainstEncryptedHistory running against a workspace whose data/history contains only non-encrypted box dirs (no notebook-crypto-backup.json and conf.json with Encrypted=false), or history dirs whose box entries fail the IsNodeIDPattern filter so nothing is collected.
Common situations: Trying to verify or migrate a KEK in a workspace where encryption was enabled but no encrypted history snapshots were ever created; history was pruned of all encrypted snapshots; pointing the tool at the wrong workspace (an unencrypted one); snapshots' .siyuan directories missing after partial copies.
Related errors
- CLI does not support encrypted notebook history
- encrypted notebook is locked, please unlock it first
- stat encrypted notebook history backup [%s] failed: %w
- check encrypted notebook history failed: %w
- cannot disable encrypted notebook feature while encrypted no
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/453c3764e95be52f.
Report an issue: GitHub.