siyuan-note/siyuan · error · errMasterPasswordMigrationPending
%w: %s (errMasterPasswordMigrationPending, Conf.Language(320
Error message
%w: %s (errMasterPasswordMigrationPending, Conf.Language(320))
What it means
In ChangeMasterPassword Phase 3, when a box's conf is missing/corrupted the code rebuilds it from the per-notebook crypt backup; if saving that rebuilt conf fails, the function returns errMasterPasswordMigrationPending wrapped with localized message 320. The global verifier and migration manifest have already been committed, so the migration is intentionally left 'pending' and will be finished by the recovery routine (recoverMasterPasswordMigration) on next startup. The error signals the user must restart so recovery can complete the per-box phase.
Source
Thrown at kernel/model/crypto.go:1810
Conf.NotebookCrypto.HistoryKEKs = newHistoryKEKs
Conf.m.Unlock()
// Conf.Save 内部会加 Conf.m,不能在持锁状态下调用(RWMutex 不可重入)
Conf.Save()
// Phase 3: 写入各 box conf + backup
for _, entry := range entries {
box := &Box{ID: entry.BoxID}
boxConf := box.GetConf()
if !boxConf.Encrypted || boxConf.BoxCrypt == nil {
// conf 缺失/损坏:尝试从 per-notebook backup 重建
backup, bErr := readNotebookCryptBackup(entry.BoxID)
if bErr == nil && backup != nil && len(backup.WrappedDEK) > 0 {
boxConf = box.GetConf()
boxConf.Encrypted = true
boxConf.BoxCrypt = backup
if saveErr := box.SaveConf(boxConf); saveErr != nil {
return fmt.Errorf("%w: %s", errMasterPasswordMigrationPending,
fmt.Sprintf(Conf.Language(320), entry.BoxID+": rebuild encrypted conf from backup failed: "+saveErr.Error()))
}
} else {
// conf 与 backup 均不可用:manifest 是该 box 加密密钥的权威来源,直接从 entry 重建 BoxCrypt,
// 避免改密因瞬时 conf 损坏而中断(详见 recoverMasterPasswordMigration 中的对称处理)。
logging.LogWarnf("rebuild encrypted box [%s] from migration entry (conf and backup both unavailable)", entry.BoxID)
boxConf = box.GetConf()
boxConf.Encrypted = true
boxConf.BoxCrypt = &conf.BoxEncryption{
WrappedDEK: entry.NewWrappedDEK,
WrapNonce: entry.NewWrapNonce,
Spec: entry.NewSpec,
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()))View on GitHub (pinned to 8641553a1f)
Solutions
- Restart SiYuan: the pending migration manifest triggers automatic recovery of the per-box confs on boot.
- Fix the underlying write failure (free disk space, close the app/lock holder, fix permissions on <data>/<boxID>/.siyuan/conf.json) and let recovery finish.
- If recovery keeps failing, verify the workspace data dir is writable and not open in a second instance, then restart again.
Example fix
// before: retrying password change immediately in the same session ChangeMasterPassword(old, new) // fails again, still pending // after: restart the kernel so recoverMasterPasswordMigration completes, // then confirm state before any further crypto operations
Defensive patterns
Strategy: retry
Validate before calling
// Check prerequisites before starting the migration
if filelock.IsExist(confPath) == false || !isDirWritable(util.DataDir) {
return errors.New("data dir must be writable before changing master password")
}
if len(model.CachedEncryptedDEKs()) > 0 {
return errors.New("lock all encrypted notebooks before changing master password")
} Type guard
func isPendingMigrationErr(err error) bool {
return errors.Is(err, model.ErrMasterPasswordMigrationPending)
} Try / catch
if err := model.ChangeMasterPassword(old, new); err != nil {
if errors.Is(err, model.ErrMasterPasswordMigrationPending) {
// do NOT retry with the old password; prompt the user to restart
// the kernel — recovery will complete the pending migration on boot
return err
}
return err
} Prevention
- Ensure the workspace data directory has free space and write permissions before changing the master password.
- Close other running SiYuan instances and exclude the data dir from AV locking during the change.
- Never delete or hand-edit .siyuan/conf.json or crypt backups — they are recovery inputs.
- After a pending-migration error, always restart rather than retrying the password change.
When it happens
Trigger: Calling the change-master-password API when a box conf is missing/BoxCrypt==nil, a valid per-notebook backup exists, but box.SaveConf fails (e.g. conf.json locked, unwritable data dir, disk full, filelock timeout).
Common situations: Data directory on a read-only/full disk or synced drive holding file locks; antivirus or another SiYuan instance locking .siyuan/conf.json; permission problems after restoring data as a different user.
Related errors
- master password migration is pending
- 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@8641553a1f (2026-09-11).
Data as JSON: /api/errors/67fdafb215d955da.
Report an issue: GitHub.