siyuan-note/siyuan · error
master password migration is pending: %v
Error message
master password migration is pending: %v
What it means
Thrown by deriveKEK (crypto.go:1282) during the finalization of a master-password migration: the new KEK was verified against all existing boxes (verifyKEKAgainstExistingBoxes true), migrationPending is true, but saveNotebookCryptoBackup(kek) failed while writing the new authenticated global backup. It wraps errMasterPasswordMigrationPending with the underlying save error, so the migration stays pending and will be retried on the next correct-password authentication.
Source
Thrown at kernel/model/crypto.go:1282
Conf.m.Unlock()
Conf.Save()
logging.LogInfof("repaired notebook crypto configuration from authenticated backup")
} else if !backupAuthenticated {
// 同步备份可能属于另一轮完整改密;只要本地配置仍与全部笔记本一致,就继续使用本地配置,
// 不覆盖候选备份,等待其余 WrappedDEK 同步完成后由新密码采用。
logging.LogWarnf("notebook crypto backup differs from usable local configuration; keeping both candidates")
}
}
if migrationPending {
// 崩溃恢复后的首次新密码验证:确认所有笔记本都已切换到新 KEK,再生成带认证的全局备份并结束迁移。
if !verifyKEKAgainstExistingBoxes(kek) {
zeroAndClear(kek)
return nil, errMasterPasswordMigrationPending
}
if err = saveNotebookCryptoBackup(kek); err != nil {
zeroAndClear(kek)
return nil, fmt.Errorf("%w: %v", errMasterPasswordMigrationPending, err)
}
removeMasterPasswordMigration()
}
return kek, nil
}
// decryptBoxCrypt 用 KEK 解密 box 的 WrappedDEK。优先使用 GetBoxEncryption 的结果(conf → backup fallback),
// 若解密失败则尝试 backup 中不同的 WrappedDEK。
// 返回解密后的 DEK 和实际使用的 BoxCrypt(可能来自 backup)。
// 若 backup 被使用会自动修复 conf.json 和刷新 backup。
func decryptBoxCrypt(boxID string, kek []byte) (dek []byte, boxCrypt *conf.BoxEncryption, err error) {
boxCrypt, err = GetBoxEncryption(boxID)
if err != nil || boxCrypt == nil || len(boxCrypt.WrappedDEK) == 0 {
return nil, nil, fmt.Errorf("no encrypted key material for box [%s]", boxID)
}
dek, err = decryptWrappedDEK(boxID, boxCrypt, kek)
if err == nil {View on GitHub (pinned to 251596fc0d)
Solutions
- Fix the underlying I/O error on the backup path (free space, permissions, locks), then re-authenticate with the new master password to let migration finalization retry.
- Do NOT manually delete the migration marker; let deriveKEK re-run saveNotebookCryptoBackup once the filesystem is healthy.
- Inspect the wrapped error tail to identify the exact syscall/path failure.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: ensure backup path is writable so migration can finalize.
func backupWritable() error {
p := dataCryptoBackupPath()
if dir := filepath.Dir(p); dir != "" {
f, err := os.CreateTemp(dir, "probe-*")
if err != nil {
return fmt.Errorf("backup dir not writable: %w", err)
}
f.Close()
os.Remove(f.Name())
}
return nil
} Try / catch
if err := model.UnlockBox(boxID, password, boxCrypt); err != nil {
if errors.Is(err, model.ErrMasterPasswordMigrationPending) || strings.Contains(err.Error(), "migration is pending") {
// fix the I/O cause, then re-authenticate with the new password to finalize migration
respond(c, "migration still pending ("+err.Error()+"); fix storage and re-enter new password")
return
}
respond(c, err.Error())
} Prevention
- Free space / fix permissions before finalizing a password change.
- Do not delete the migration marker manually; let deriveKEK retry finalization.
- Re-authenticate with the new password once storage is healthy.
When it happens
Trigger: deriveKEK reaches the migrationPending branch after verifier+box verification pass, then saveNotebookCryptoBackup returns a non-nil I/O error writing dataCryptoBackupPath(). The migration file is not removed, so the next deriveKEK with the new password re-attempts finalization.
Common situations: Disk full, permission denied, or a locked backup file exactly when finalizing a password change. Read-only mount during migration. Antivirus locking the file on Windows.
Related errors
- enable encrypted notebook failed: failed to persist key back
- read image failed: %w
- 315
- cannot disable encrypted notebook feature while encrypted no
- 323
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/b85bde66fb1aaa4d.
Report an issue: GitHub.