siyuan-note/siyuan · error
read encrypted notebook history conf [%s] failed: %w
Error message
read encrypted notebook history conf [%s] failed: %w
What it means
isEncryptedHistoryBoxDir reads .siyuan/conf.json via filelock.ReadFile; a failed read is wrapped as 'read encrypted notebook history conf [%s] failed'. The library throws it because the BoxConf determines whether the history box holds encrypted material — a read failure must abort rather than default to 'unencrypted', which could permit unsafe deletion of recovery material.
Source
Thrown at kernel/model/crypto.go:950
// 再 fallback 到 conf.json 的 Encrypted 标志。
func isEncryptedHistoryBoxDir(boxDir string) (bool, error) {
siyuanDir := filepath.Join(boxDir, ".siyuan")
backupPath := filepath.Join(siyuanDir, "notebook-crypto-backup.json")
if _, err := os.Stat(backupPath); err == nil {
return true, nil
} else if !os.IsNotExist(err) {
return false, fmt.Errorf("stat encrypted notebook history backup [%s] failed: %w", boxDir, err)
}
confPath := filepath.Join(siyuanDir, "conf.json")
if _, err := os.Stat(confPath); err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("stat encrypted notebook history conf [%s] failed: %w", boxDir, err)
}
data, err := filelock.ReadFile(confPath)
if err != nil {
return false, fmt.Errorf("read encrypted notebook history conf [%s] failed: %w", boxDir, err)
}
var boxConf conf.BoxConf
if err = gulu.JSON.UnmarshalJSON(data, &boxConf); err != nil {
return false, fmt.Errorf("parse encrypted notebook history conf [%s] failed: %w", boxDir, err)
}
return boxConf.Encrypted, nil
}
// cachedDEKs 缓存已解锁加密笔记本的 DEK,按 boxID 索引。
// KEK 不全局缓存("严格每笔记本单独解锁"语义):UnlockBox 临时派生 KEK 解出 DEK 后即丢弃 KEK,
// 仅保留 per-box DEK 供后续读写加解密。
var (
cachedDEKs = map[string][]byte{}
cachedDEKsLock sync.RWMutex
)
// boxLastAccess 记录每个加密笔记本最近一次真实用户交互或显式保活时间(unix 纳秒),供自动锁定 cron 使用。
// key: boxID, value: *atomic.Int64。UnlockBox 成功时初始化,Unmount 时清理。View on GitHub (pinned to 8641553a1f)
Solutions
- Close other SiYuan instances/scripts locking the workspace, then retry
- Verify conf.json still exists and is readable after the stat (fix the delete race by pausing history cleanup)
- Read the wrapped cause; if it is an I/O/mount error, repair the storage first
- Restore conf.json from backup if the file itself is unreadable or truncated
Example fix
// before: second kernel instance locks the workspace pkill -f siyuan-kernel # all instances // after: keep exactly one instance owning workspace locks systemctl --user stop siyuan-old; run new kernel; retry scan
Defensive patterns
Strategy: retry
Validate before calling
if _, err := os.Stat(confPath); err == nil {
if f, err := os.Open(confPath); err != nil { return err } else { f.Close() }
} Try / catch
enc, err := isEncryptedHistoryBoxDir(boxDir)
if err != nil && isLockOrTransient(err) {
time.Sleep(100 * time.Millisecond)
enc, err = isEncryptedHistoryBoxDir(boxDir) // retry once after releasing other instances
} Prevention
- Run only one kernel instance per workspace
- Pause history-pruning jobs during scans so files are not deleted mid-read
- Ensure filelock compatibility of the storage (avoid filesystems without proper locking, e.g. some NFS setups)
- Retry transient read failures before concluding the box is unencrypted
When it happens
Trigger: Callers of isEncryptedHistoryBoxDir hitting read errors on conf.json: the file is locked exclusively by another process, permission denied at open time (stat succeeded, open fails), or filelock failing on the underlying file (removed between stat and read, or filesystem-level I/O error).
Common situations: Two kernel instances (or a kernel plus a tooling script) running on the same workspace; the file deleted by history pruning between the stat and read; disk errors or a forcibly-unmounted network volume.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- save session file failed: %w
- read AI editor actions failed: %w
- write AI editor actions failed: %w
- read history dir failed: %w
- read history snapshot [%s] failed: %w
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/716f6d09af9717d4.
Report an issue: GitHub.