siyuan-note/siyuan · critical
encrypted notebook metadata verification failed after write
Error message
encrypted notebook metadata verification failed after write
What it means
Returned by CreateEncryptedBox when, after SaveConf and writeNotebookCryptBackup both report success, a readback (box.GetConf) shows the encrypted configuration is NOT actually persisted — Encrypted is false or BoxCrypt is nil. This is a data-integrity safety net: it prevents a notebook from being treated as plain when it should be encrypted (which would cause plaintext writes to what is meant to be an encrypted store).
Source
Thrown at kernel/model/crypto.go:2588
}
box := &Box{ID: id}
boxConf := box.GetConf()
boxConf.Encrypted = true
boxConf.BoxCrypt = enc
if err = encryptBoxMetadata(id, boxConf, dek); err != nil {
return "", fmt.Errorf("encrypt notebook metadata failed: %w", err)
}
if err = box.SaveConf(boxConf); err != nil {
return "", fmt.Errorf("save encrypted notebook conf failed: %w", err)
}
if err = writeNotebookCryptBackup(id, enc); err != nil {
return "", fmt.Errorf("write notebook crypt backup failed: %w", err)
}
// 回读校验加密配置已落盘,避免写失败后按普通笔记本处理
verifyConf := box.GetConf()
if verifyConf == nil || !verifyConf.Encrypted || verifyConf.BoxCrypt == nil {
err = errors.New("encrypted notebook metadata verification failed after write")
return "", err
}
markRuntimeEncryptedBox(id)
invalidateEncryptedPublishAccessCache()
// 复用刚派生的 DEK 直接开 db + 缓存,省去再次 Argon2id 解锁
cachedDEKsLock.Lock()
if err = sql.OpenEncryptedDB(id, dek); err != nil {
cachedDEKsLock.Unlock()
return "", err
}
if err = treenode.OpenEncryptedBlockTreeDB(id, dek); err != nil {
sql.CloseEncryptedDB(id)
cachedDEKsLock.Unlock()
return "", err
}
cachedDEKs[id] = dek
cachedDEKsLock.Unlock()View on GitHub (pinned to 251596fc0d)
Solutions
- Ensure only one kernel instance is running against the workspace.
- Check for concurrent code paths that write to the same notebook's .syconf and serialize them with notebookCryptoMu or a per-box lock.
- Examine filesystem sync semantics — if using a network FS, ensure writes are fsync'd before the readback.
Defensive patterns
Strategy: validation
Try / catch
id, err := model.CreateEncryptedBox(name, password)
if err != nil {
if strings.Contains(err.Error(), "verification failed after write") {
// integrity violation — possible concurrent writer or FS caching issue
logging.LogErrorf("critical: encrypted conf did not persist for box %s", id)
}
return err
} Prevention
- Never run two kernel instances against the same workspace.
- Serialize all .syconf writers with notebookCryptoMu or a per-box lock.
- On network filesystems, ensure fsync semantics so readback reflects the write.
When it happens
Trigger: SaveConf reported success but the file wasn't actually written or was overwritten by another process between write and readback. A race with a concurrent conf writer, a stale file-cache, or a filesystem that acknowledged a write before flushing.
Common situations: Concurrent operations modifying the same notebook's .syconf. Filesystem caching layer that delays persistence. Another kernel instance writing to the same workspace (unsupported but happens in misconfigured setups).
Related errors
- Encrypted notebook feature is not enabled
- encrypt notebook metadata failed: %w
- save encrypted notebook conf failed: %w
- write notebook crypt backup failed: %w
- initialize encrypted notebook document failed: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/04ed48bfcee3beab.
Report an issue: GitHub.