{"record":{"id":"4051cec5b5c596e6","repo":"siyuan-note/siyuan","slug":"master-password-migration-is-pending-master-passw-4051ce","errorCode":null,"errorMessage":"master password migration is pending: Master password change partially failed. Please restart SiYuan to complete recovery. Detail: save notebook crypto backup failed: %s","messagePattern":"master password migration is pending: Master password change partially failed\\. Please restart SiYuan to complete recovery\\. Detail: save notebook crypto backup failed: (.+?)","errorType":"exception","errorClass":"errMasterPasswordMigrationPending","httpStatus":null,"severity":"critical","filePath":"kernel/model/crypto.go","lineNumber":1821,"sourceCode":"\t\t\t}\n\t\t}\n\t\tboxConf.BoxCrypt.WrappedDEK = entry.NewWrappedDEK\n\t\tboxConf.BoxCrypt.Spec = entry.NewSpec\n\t\tboxConf.BoxCrypt.WrapNonce = entry.NewWrapNonce\n\t\tboxConf.BoxCrypt.Metadata = append([]byte(nil), entry.Metadata...)\n\t\tif err = box.SaveConf(boxConf); err != nil {\n\t\t\treturn fmt.Errorf(\"%w: %s\", errMasterPasswordMigrationPending,\n\t\t\t\tfmt.Sprintf(Conf.Language(320), entry.BoxID+\": save conf failed: \"+err.Error()))\n\t\t}\n\t\tif err = writeNotebookCryptBackup(entry.BoxID, boxConf.BoxCrypt); err != nil {\n\t\t\treturn fmt.Errorf(\"%w: %s\", errMasterPasswordMigrationPending,\n\t\t\t\tfmt.Sprintf(Conf.Language(320), entry.BoxID+\": update notebook crypt backup failed: \"+err.Error()))\n\t\t}\n\t}\n\n\t// Phase 4: 先持久化全局备份，再清除 manifest，确保崩溃后可恢复\n\tif err = saveNotebookCryptoBackup(newKEK); err != nil {\n\t\treturn fmt.Errorf(\"%w: %s\", errMasterPasswordMigrationPending,\n\t\t\tfmt.Sprintf(Conf.Language(320), \"save notebook crypto backup failed: \"+err.Error()))\n\t}\n\tremoveMasterPasswordMigration()\n\tIncSync()\n\treturn nil\n}\n\n// IsEncryptedBox 判断给定 boxID 是否为加密笔记本。\n// 配置缺失或损坏时依次检查运行时身份、独立备份和密文标识，任何检查错误都按加密笔记本处理。\nfunc IsEncryptedBox(boxID string) bool {\n\tif !ast.IsNodeIDPattern(boxID) {\n\t\treturn false\n\t}\n\tif isRuntimeEncryptedBox(boxID) {\n\t\treturn true\n\t}\n\tif isRuntimeNormalBox(boxID) {\n\t\treturn false","sourceCodeStart":1803,"sourceCodeEnd":1839,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/crypto.go#L1803-L1839","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isMigrationPendingErr(err error) bool {\n    return errors.Is(err, errMasterPasswordMigrationPending) // in-package; outside use string prefix check\n}","tryCatchPattern":"if err := model.ChangeMasterPassword(oldPw, newPw); isMigrationPendingErr(err) {\n    // notebooks migrated but global backup failed: manifest retained on purpose,\n    // restart + verify with the new password runs exactly this last step again\n    instructUserRestart()\n    return\n}","preventionTips":["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"],"tags":["encryption","master-password","migration","io","backup"],"backgroundTag":"master-password-migration-pending","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}