siyuan-note/siyuan · critical · errMasterPasswordMigrationPending
master password migration is pending: Master password change
Error message
master password migration is pending: Master password change partially failed. Please restart SiYuan to complete recovery. Detail: %s: update notebook crypt backup failed: %s
What it means
Returned by ChangeMasterPassword Phase 3 (crypto.go:1813-1816) when writeNotebookCryptBackup fails after the notebook's conf.json was already updated to the new key material. The per-notebook backup is the redundancy that lets decryptBoxCrypt fall back and repair conf, so failing to refresh it aborts with errMasterPasswordMigrationPending (localized 320); the manifest survives for restart recovery.
Source
Thrown at kernel/model/crypto.go:1814
Metadata: entry.Metadata,
CreatedAt: time.Now().UnixMilli(),
}
if saveErr := box.SaveConf(boxConf); saveErr != nil {
return fmt.Errorf("%w: %s", errMasterPasswordMigrationPending,
fmt.Sprintf(Conf.Language(320), entry.BoxID+": rebuild encrypted conf from migration entry failed: "+saveErr.Error()))
}
}
}
boxConf.BoxCrypt.WrappedDEK = entry.NewWrappedDEK
boxConf.BoxCrypt.Spec = entry.NewSpec
boxConf.BoxCrypt.WrapNonce = entry.NewWrapNonce
boxConf.BoxCrypt.Metadata = append([]byte(nil), entry.Metadata...)
if err = box.SaveConf(boxConf); err != nil {
return fmt.Errorf("%w: %s", errMasterPasswordMigrationPending,
fmt.Sprintf(Conf.Language(320), entry.BoxID+": save conf failed: "+err.Error()))
}
if err = writeNotebookCryptBackup(entry.BoxID, boxConf.BoxCrypt); err != nil {
return fmt.Errorf("%w: %s", errMasterPasswordMigrationPending,
fmt.Sprintf(Conf.Language(320), entry.BoxID+": update notebook crypt backup failed: "+err.Error()))
}
}
// Phase 4: 先持久化全局备份,再清除 manifest,确保崩溃后可恢复
if err = saveNotebookCryptoBackup(newKEK); err != nil {
return fmt.Errorf("%w: %s", errMasterPasswordMigrationPending,
fmt.Sprintf(Conf.Language(320), "save notebook crypto backup failed: "+err.Error()))
}
removeMasterPasswordMigration()
IncSync()
return nil
}
// IsEncryptedBox 判断给定 boxID 是否为加密笔记本。
// 配置缺失或损坏时依次检查运行时身份、独立备份和密文标识,任何检查错误都按加密笔记本处理。
func IsEncryptedBox(boxID string) bool {
if !ast.IsNodeIDPattern(boxID) {View on GitHub (pinned to afa823b6b4)
Solutions
- Restore writability of the notebook crypt backup location named in the logs
- Restart SiYuan - recovery re-runs the manifest and rewrites both conf and backups; verify with the new password afterwards
- Keep enough free space and pause third-party file locks before rotating the master password
Defensive patterns
Strategy: type-guard
Type guard
func isMigrationPendingErr(err error) bool {
return errors.Is(err, errMasterPasswordMigrationPending) // in-package; outside use string prefix check
} Try / catch
if err := model.ChangeMasterPassword(oldPw, newPw); isMigrationPendingErr(err) {
instructUserRestart() // backup refresh failed; manifest recovery rewrites conf+backup pairs
return
} Prevention
- Verify the notebook crypt backup directory is writable before starting the change
- Do not mark backup files read-only with external backup tools while SiYuan runs
- Keep free disk headroom for every per-notebook backup rewrite
When it happens
Trigger: The backup file under the notebook crypto backup path cannot be written: disk full, permission denied, or the backup file locked by another process during the change loop.
Common situations: Backup directory made read-only or excluded incorrectly; disk exhaustion partway through the per-notebook backup writes; file-level locks from backup software.
Related errors
- master password migration is pending: Master password change
- master password migration is pending: %v
- master password migration is pending: Master password change
- master password migration is pending: Master password change
- master password migration is pending: Master password change
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/b5f564c669e27959.
Report an issue: GitHub.