siyuan-note/siyuan · error

marshal master password migration failed: %w

Error message

marshal master password migration failed: %w

What it means

gulu.JSON.MarshalIndentJSON failed while serializing the masterPasswordMigration struct for disk persistence. The migration record could not be converted to JSON, so nothing is written and the error is wrapped and returned.

Source

Thrown at kernel/model/crypto.go:544

func masterPasswordMigrationPath() string {
	return filepath.Join(util.DataDir, ".siyuan", "master-password-migration.json")
}

func writeMasterPasswordMigration(m *masterPasswordMigration) error {
	masterPasswordMigrationMu.Lock()
	defer masterPasswordMigrationMu.Unlock()
	return writeMasterPasswordMigrationUnlocked(m)
}

func writeMasterPasswordMigrationUnlocked(m *masterPasswordMigration) error {
	p := masterPasswordMigrationPath()
	if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil {
		return fmt.Errorf("mkdir master password migration dir failed: %w", err)
	}
	data, err := gulu.JSON.MarshalIndentJSON(m, "", "  ")
	if err != nil {
		return fmt.Errorf("marshal master password migration failed: %w", err)
	}
	return filelock.WriteFile(p, data)
}

func readMasterPasswordMigration() (*masterPasswordMigration, error) {
	masterPasswordMigrationMu.Lock()
	defer masterPasswordMigrationMu.Unlock()
	return readMasterPasswordMigrationUnlocked()
}

func readMasterPasswordMigrationUnlocked() (*masterPasswordMigration, error) {
	p := masterPasswordMigrationPath()
	if !filelock.IsExist(p) {
		return nil, nil
	}
	data, err := filelock.ReadFile(p)
	if err != nil {
		return nil, fmt.Errorf("read master password migration failed: %w", err)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Inspect the wrapped error to find the offending field in masterPasswordMigration
  2. Ensure migration struct fields are JSON-serializable (no chan/func/cyclic refs)
  3. Rebuild the migration record from validated inputs rather than reusing a possibly-corrupt struct
  4. Update the kernel if a released version introduced the serialization defect

Example fix

// before: unmarshalable field
m.Callback = func() {} // marshal fails
// after: store only data
m.Unlocked = true
err := writeMasterPasswordMigration(m)
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := gulu.JSON.MarshalIndentJSON(m, "", "  "); err != nil {
    return fmt.Errorf("migration record not serializable: %w", err)
}

Try / catch

if err := writeMasterPasswordMigration(m); err != nil {
    var tErr *json.UnsupportedTypeError
    if errors.As(err, &tErr) {
        log.Printf("bad migration field type: %v", tErr.Type)
    }
    return err
}

Prevention

When it happens

Trigger: writeMasterPasswordMigrationUnlocked marshals *masterPasswordMigration via gulu.JSON.MarshalIndentJSON and the marshaller returns an error — typically an unmarshalable nested value or a custom marshaller failure.

Common situations: A masterPasswordMigration struct extended with non-JSON-safe fields, or corrupted in-memory migration state assembled from a failed unlock operation.

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/362174ecc6cd5bdb. Report an issue: GitHub.