siyuan-note/siyuan · error
unmarshal notebook crypt backup failed: %w
Error message
unmarshal notebook crypt backup failed: %w
What it means
Wrapped error from gulu.JSON.UnmarshalJSON while parsing the notebook crypto backup file. The bytes were read successfully but are not a valid conf.BoxEncryption JSON — hand-edited, truncated, zero-filled (sparsed by sync tools), or written by an incompatible version with a different schema.
Source
Thrown at kernel/model/crypto.go:2493
func readNotebookCryptBackup(boxID string) (*conf.BoxEncryption, error) {
if !ast.IsNodeIDPattern(boxID) {
return nil, errors.New("invalid notebook ID")
}
backupPath := notebookCryptoBackupPath(boxID)
if !filelock.IsExist(backupPath) {
return nil, nil
}
return readBoxEncryptionFile(backupPath)
}
func readBoxEncryptionFile(backupPath string) (*conf.BoxEncryption, error) {
data, err := filelock.ReadFile(backupPath)
if err != nil {
return nil, fmt.Errorf("read notebook crypt backup failed: %w", err)
}
var crypt conf.BoxEncryption
if err = gulu.JSON.UnmarshalJSON(data, &crypt); err != nil {
return nil, fmt.Errorf("unmarshal notebook crypt backup failed: %w", err)
}
if err = validateBoxEncryption(&crypt); err != nil {
return nil, err
}
return &crypt, nil
}
// copyAssetDecryptIfEncrypted 把 srcPath 的 asset 复制到 destPath。
// 若 srcPath 在已解锁的加密笔记本下,读密文→解密→写明文到 destPath(导出目录);
// 否则走 filelock.Copy 原路径(字节级复制,密文/明文均可)。
func copyAssetDecryptIfEncrypted(srcPath, destPath string) error {
if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
return err
}
boxID := ExtractBoxIDFromAssetsPath(srcPath)
if boxID != "" && IsEncryptedBox(boxID) {
HoldBoxReadLock(boxID)View on GitHub (pinned to afa823b6b4)
Solutions
- Restore the backup file from a workspace snapshot (history/backup tooling)
- Reconstruct it from conf.json: copy the BoxCrypt object of that notebook into .siyuan/notebook-crypto-backup.json with correct shape, then run validateBoxEncryption-compatible unlock
- As a last resort, decrypt-and-recreate: unlock with the known-good conf.json copy, remove the broken backup, and run ChangeMasterPassword to regenerate both files
Defensive patterns
Strategy: try-catch
Try / catch
if err != nil {
if strings.Contains(err.Error(), "unmarshal notebook crypt backup failed") {
// reconstruct backup from conf.json BoxCrypt or restore from snapshot, then retry unlock Prevention
- Never hand-edit notebook-crypto-backup.json
- Keep version-consistent backups: copy conf.json and the backup file together
- Beware cloud-sync placeholder files materializing as zero-length JSON
When it happens
Trigger: readNotebookCryptBackup on a notebook-crypto-backup.json that was edited manually or corrupted; files truncated by interrupted writes; a workspace synced via a tool that placeholder-izes files.
Common situations: Users hand-editing JSON to 'fix' encryption; cloud-sync placeholder files materialized as zeros; version mismatches after downgrades.
Related errors
- marshal notebook crypt backup failed: %w
- Conf.Language(317)
- invalid session id
- encode session data failed: %w
- unmarshal legacy AI editor actions failed: %w
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/4b2d0e4fd17d6a9e.
Report an issue: GitHub.