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: save notebook crypto backup failed: %s
What it means
Returned by ChangeMasterPassword Phase 4 (crypto.go:1819-1823) when the final authenticated global notebook-crypto backup (saveNotebookCryptoBackup) fails after all per-notebook confs and backups were already migrated to the new KEK. Because the manifest is only removed after this write succeeds, the change is deliberately left 'pending'; restarting and verifying the new password re-attempts exactly this step.
Source
Thrown at kernel/model/crypto.go:1821
}
}
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) {
return false
}
if isRuntimeEncryptedBox(boxID) {
return true
}
if isRuntimeNormalBox(boxID) {
return falseView on GitHub (pinned to afa823b6b4)
Solutions
- Fix the write condition on the global backup path (space, permissions, locks)
- Restart SiYuan and submit the new master password - verification detects the pending manifest, re-checks the boxes and retries saveNotebookCryptoBackup
- Only after recovery completes (no more 'migration is pending' on verification) treat the password change as done
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) {
// notebooks migrated but global backup failed: manifest retained on purpose,
// restart + verify with the new password runs exactly this last step again
instructUserRestart()
return
} Prevention
- Check writability of the global notebook crypto backup path before the change
- Reserve disk space for the final global backup write even after all notebook writes succeed
- After restart, confirm verification no longer reports 'migration is pending' before further password operations
When it happens
Trigger: Global backup file write failure at the very end of the change: disk full, permission denied, or a lock on the global notebook crypto backup path - after all notebook-level writes already succeeded.
Common situations: Disk hitting its limit at the last write of a long migration; AV quarantining/locking the global backup file; workspace directory permission changed mid-operation.
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/4051cec5b5c596e6.
Report an issue: GitHub.