{"record":{"id":"7573baf35a1bb26c","repo":"siyuan-note/siyuan","slug":"master-password-migration-is-pending","errorCode":null,"errorMessage":"master password migration is pending","messagePattern":"master password migration is pending","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/model/crypto.go","lineNumber":166,"sourceCode":"\t})\n\tif errors.Is(err, errEncryptedNotebookPayloadFound) {\n\t\treturn true, nil\n\t}\n\treturn false, err\n}\n\nconst encryptedAssetMetadataMaxSize = 1024 * 1024\nconst encryptedAssetChunkSize = 1024 * 1024\nconst encryptedAssetChunkMaxCiphertextSize = encryptedAssetChunkSize + 1024\n\ntype encryptedAssetMetadata struct {\n\tOriginalName string `json:\"originalName\"`\n\tSize         int64  `json:\"size\"`\n\tChunks       uint64 `json:\"chunks\"`\n}\n\n// errMasterPasswordMigrationPending 表示改密已切换全局 verifier，但部分笔记本配置尚待恢复。\nvar errMasterPasswordMigrationPending = errors.New(\"master password migration is pending\")\n\n// notebookCryptoMu 串行化加密笔记本的控制面操作（Enable/Disable/Create/ChangeMasterPassword/Import/restore 等），\n// 避免 ChangeMasterPassword 枚举与 CreateEncryptedBox 并发导致新笔记本用旧 KEK 但 verifier 已切换的不可恢复状态。\nvar notebookCryptoMu sync.Mutex\n\nvar masterPasswordMigrationMu sync.Mutex\n\n// boxLifecycleLocks 为每个 box 提供一个 RWMutex，协调锁定操作与在途解密请求。\n// 在途解密请求持读锁，LockBox 持写锁，确保锁定后不会有新的解密输出。\nvar boxLifecycleLocks = sync.Map{} // map[string]*sync.RWMutex\n\nfunc acquireBoxReadLock(boxID string) {\n\tmuI, _ := boxLifecycleLocks.LoadOrStore(boxID, &sync.RWMutex{})\n\tmuI.(*sync.RWMutex).RLock()\n}\n\nfunc releaseBoxReadLock(boxID string) {\n\tif muI, ok := boxLifecycleLocks.Load(boxID); ok {","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/model/crypto.go#L148-L184","documentation":"errMasterPasswordMigrationPending is a sentinel marking an unfinished master-password change: the global verifier was already switched to the new KEK, but some per-notebook WrappedDEK material or the global backup has not been fully persisted (crash mid-migration, or a per-box conf/backup save failing in ChangeMasterPassword phases 3-4). It is returned bare when recovery still cannot verify all boxes against the new KEK, and wrapped with box-level detail (%w) when re-writing a box conf, per-notebook backup, or the global backup fails during recovery/continuation.","triggerScenarios":"kernel killed or crashed between writeMasterPasswordMigration and removeMasterPasswordMigration; disk full or filelock contention while ChangeMasterPassword rewrites box confs; then the next unlock with the new password fails verifyKEKAgainstExistingBoxes or saveNotebookCryptoBackup and returns this sentinel (kernel/model/crypto.go:1279,1283,1783-1821).","commonSituations":"Force-quitting the app during a master password change; workspace on a flaky/synced drive where conf writes fail; two operations racing (the code serializes via notebookCryptoMu/masterPasswordMigrationMu, but external processes bypass that).","solutions":["Unlock/verify again with the NEW master password - the recovery path (recoverMasterPasswordMigration) re-runs and completes the pending writes","Make sure the workspace disk is writable and no second kernel instance or sync tool is touching conf files, then retry","Do not attempt the old password - the global verifier has already switched, which is exactly what this sentinel reports","Once unlocked, export the key backup (ExportNotebookCryptoBackup) so a future interruption is recoverable offline"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"err := model.CheckMasterPassword(password) // or the unlock flow\nif err != nil {\n    if strings.Contains(err.Error(), \"master password migration is pending\") {\n        // interrupted change-password: retry with the NEW password until recovery completes; never fall back to the old one\n    }\n}","preventionTips":["Never force-quit the kernel while a master password change is in progress","Run only one kernel instance per workspace so migration manifest writes do not race","Keep the workspace disk writable; conf/backup write failures are the main non-crash trigger","Export the key backup right after enabling or changing the master password"],"tags":["crypto","master-password","encryption","migration","crash-recovery","go","siyuan"],"backgroundTag":"interrupted-migration-recovery","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}