siyuan-note/siyuan · error
marshal notebook crypt backup failed: %w
Error message
marshal notebook crypt backup failed: %w
What it means
Wrapped error from gulu.JSON.MarshalIndentJSON while serializing the conf.BoxEncryption struct (WrappedDEK, WrapNonce, KDF params) for the per-notebook backup. Since BoxEncryption is a plain data struct, a marshal failure is essentially unreachable in practice and signals a programming error (unsupported field type added to the struct) rather than an environment problem.
Source
Thrown at kernel/model/crypto.go:2465
return filepath.Join(util.DataDir, boxID, ".siyuan", notebookCryptoBackupFilename)
}
// writeNotebookCryptBackup 写入加密笔记本的 BoxCrypt 备份。
// 仅在 Encrypted=true 的笔记本上调用,配合 CreateEncryptedBox / ChangeMasterPassword 写入。
func writeNotebookCryptBackup(boxID string, crypt *conf.BoxEncryption) error {
if !ast.IsNodeIDPattern(boxID) {
return errors.New("invalid notebook ID")
}
if err := validateBoxEncryption(crypt); err != nil {
return err
}
backupPath := notebookCryptoBackupPath(boxID)
if err := os.MkdirAll(filepath.Dir(backupPath), 0755); err != nil {
return fmt.Errorf("mkdir notebook crypt backup dir failed: %w", err)
}
data, err := gulu.JSON.MarshalIndentJSON(crypt, "", " ")
if err != nil {
return fmt.Errorf("marshal notebook crypt backup failed: %w", err)
}
if err := filelock.WriteFile(backupPath, data); err != nil {
return fmt.Errorf("write notebook crypt backup failed: %w", err)
}
return nil
}
// readNotebookCryptBackup 读取加密笔记本的 BoxCrypt 备份。
// 备份文件不存在时返回 (nil, nil),调用方据此区分"非加密笔记本"和"备份不存在"。
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)View on GitHub (pinned to afa823b6b4)
Solutions
- Check the wrapped error for json.UnsupportedTypeError and remove/replace the offending field type in conf.BoxEncryption
- Keep BoxEncryption limited to strings, numbers and byte slices
- Treat this error as a build-time bug: add a unit test asserting MarshalIndentJSON succeeds on a valid validateBoxEncryption sample
Defensive patterns
Strategy: try-catch
Try / catch
var typeErr *json.UnsupportedTypeError
if errors.As(err, &typeErr) {
// struct contains an unmarshalable field: fix conf.BoxEncryption Prevention
- Keep conf.BoxEncryption composed only of JSON-safe field types
- Add a round-trip marshal test for the encryption config structs in CI
When it happens
Trigger: A future field of type chan/func/complex added to conf.BoxEncryption; a custom fork that embeds unmarshalable types in the struct.
Common situations: Almost exclusively hit by developers modifying the struct; not a runtime condition users encounter.
Related errors
- unmarshal notebook crypt backup failed: %w
- Conf.Language(317)
- encode session data failed: %w
- unmarshal legacy AI editor actions failed: %w
- marshal legacy AI editor actions failed: %w
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/1d0169af39d1744d.
Report an issue: GitHub.