siyuan-note/siyuan · error

marshal notebook crypto backup failed: %w

Error message

marshal notebook crypto backup failed: %w

What it means

saveNotebookCryptoBackup marshals the NotebookCrypto configuration to JSON before the atomic write; this error wraps any json.Marshal failure. This is nearly impossible in practice because the struct contains only JSON-serializable fields, but if it happens (e.g. an unsupported field type introduced by code change or corruption) no backup is written.

Source

Thrown at kernel/model/crypto.go:404

	prepareBackupForWrite(&nc)
	nc.KEKMAC = computeKEKMAC(&nc, kek)
	if !notebookCryptoConfigurationComplete(&nc) {
		Conf.m.Unlock()
		return errors.New("cannot save incomplete notebook crypto configuration")
	}
	Conf.NotebookCrypto.Spec = nc.Spec
	Conf.NotebookCrypto.BackupID = nc.BackupID
	Conf.NotebookCrypto.CreatedAt = nc.CreatedAt
	Conf.NotebookCrypto.Checksum = nc.Checksum
	Conf.NotebookCrypto.KEKMAC = nc.KEKMAC // 保持 Conf 与备份文件的 KEKMAC 一致
	Conf.m.Unlock()
	backupPath := dataCryptoBackupPath()
	if err := os.MkdirAll(filepath.Dir(backupPath), 0755); err != nil {
		return fmt.Errorf("mkdir notebook crypto backup dir failed: %w", err)
	}
	data, err := json.Marshal(nc)
	if err != nil {
		return fmt.Errorf("marshal notebook crypto backup failed: %w", err)
	}
	if err := atomicWriteFile(backupPath, data); err != nil {
		return fmt.Errorf("write notebook crypto backup failed: %w", err)
	}
	return nil
}

// writeNotebookCryptoBackupData 将指定的 NotebookCrypto 写入备份文件(不依赖 Conf.NotebookCrypto)。
// kek 必须非 nil:在 Checksum 定型后计算 KEKMAC,保证落盘 MAC 与落盘内容一致。
func writeNotebookCryptoBackupData(nc *conf.NotebookCrypto, kek []byte) error {
	if kek == nil {
		return errors.New("cannot generate notebook crypto backup without KEK")
	}
	prepareBackupForWrite(nc)
	nc.KEKMAC = computeKEKMAC(nc, kek)
	if !notebookCryptoConfigurationComplete(nc) {
		return errors.New("cannot write incomplete notebook crypto backup")
	}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Check for modifications or forks of conf.NotebookCrypto that introduce unserializable fields
  2. Ensure no plugin/patch overrides MarshalJSON for NotebookCrypto
  3. Retry after restoring a stock kernel build; if reproducible, capture the wrapped inner error for a bug report
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(Conf.NotebookCrypto); err != nil {
    // config struct not serializable; do not attempt backup save
}

Try / catch

if err := saveNotebookCryptoBackup(kek); err != nil {
    if strings.Contains(err.Error(), "marshal notebook crypto backup failed") {
        // inspect wrapped inner error; suspect struct/marshaler changes
    }
}

Prevention

When it happens

Trigger: json.Marshal(nc) returns an error inside saveNotebookCryptoBackup, called from EnableEncryptedNotebook, deriveKEK, or ChangeMasterPassword.

Common situations: A custom marshaler on conf.NotebookCrypto panicking or returning an error; an incompatible struct change (e.g. a func or channel field added) after a code upgrade; extremely rare in a released build.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/f6817a8d749bbadf. Report an issue: GitHub.