{"record":{"id":"e9e302a0eeebe6db","repo":"siyuan-note/siyuan","slug":"conf-language-316-box-id","errorCode":null,"errorMessage":"Conf.Language(316) + \" [box=\" + id + \"]\"","messagePattern":"Conf\\.Language\\(316\\) \\+ \" \\[box=\" \\+ id \\+ \"\\]\"","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/crypto.go","lineNumber":1757,"sourceCode":"\tif err != nil {\n\t\treturn err\n\t}\n\tnewVerifier, err := util.EncryptWithAAD(newKEK, kekVerifierMagic, []byte(\"siyuan:kek-verifier\"))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Phase 0: 遍历所有加密笔记本（含 conf 损坏但存在备份的），预计算新 WrappedDEK（内存操作）\n\t// 允许 entries 为空：用户可能已启用加密功能但尚未创建加密笔记本，此时仍需更新全局 verifier 和 backup。\n\tencBoxIDs, listErr := listAllEncryptedBoxIDs()\n\tif listErr != nil {\n\t\treturn fmt.Errorf(\"list encrypted notebooks failed: %w\", listErr)\n\t}\n\tvar entries []migrationBoxEntry\n\tfor _, id := range encBoxIDs {\n\t\tdek, boxCrypt, dErr := decryptBoxCrypt(id, oldKEK)\n\t\tif dErr != nil {\n\t\t\treturn errors.New(Conf.Language(316) + \" [box=\" + id + \"]\")\n\t\t}\n\t\tnewWrapped, nErr := util.EncryptWithAAD(newKEK, dek, wrappedDEKAAD(id))\n\t\tzeroAndClear(dek)\n\t\tif nErr != nil {\n\t\t\treturn nErr\n\t\t}\n\t\tentries = append(entries, migrationBoxEntry{\n\t\t\tBoxID:         id,\n\t\t\tNewSpec:       boxEncryptionSpec,\n\t\t\tNewWrappedDEK: newWrapped,\n\t\t\tNewWrapNonce:  mustEncryptionNonce(newWrapped),\n\t\t\tMetadata:      append([]byte(nil), boxCrypt.Metadata...),\n\t\t})\n\t}\n\n\t// Phase 1: 持久化 migration manifest（崩溃后 recovery 的依据）\n\tnewParamsJSON, _ := gulu.JSON.MarshalJSON(params)\n\tmig := &masterPasswordMigration{","sourceCodeStart":1739,"sourceCodeEnd":1775,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto.go#L1739-L1775","documentation":"During ChangeMasterPassword, each encrypted notebook's data-encryption key (DEK) is unwrapped with a key derived from the OLD master password (decryptBoxCrypt). If that per-box unwrap fails, the change aborts with localized message 316 plus the offending box ID, because re-wrapping a DEK under the new KEK requires authenticating it with the old one first. Nothing has been written yet (this is Phase 0), so no migration state is left behind.","triggerScenarios":"Calling the master-password change API when decryptBoxCrypt(boxID, oldKEK) fails for any listed encrypted notebook: the supplied old password is wrong, the box's conf.json/.siyuan crypt data (WrappedDEK/nonce/spec) is corrupted or truncated, or the box crypt was written with a different KDF/AAD scheme than expected.","commonSituations":"User typos or misremembers the old master password; a notebook's .siyuan/conf.json was partially written by a crash or edited/synced inconsistently; a box was carried over from an older SiYuan version with a different box-crypt format; backup-restore mixed encrypted boxes with a mismatched global MasterSalt.","solutions":["Re-run the change with the correct old master password; verify it by unlocking an encrypted notebook first.","Identify the box from the [box=ID] suffix, inspect <data>/<ID>/.siyuan/conf.json for corruption, and restore that notebook's conf (or the whole box) from a known-good backup/snapshot.","If the box is no longer needed or is recoverable, decrypt/export or remove it, then retry the password change.","Check that the workspace was not switched mid-way (MasterSalt/KDFParams belong to the workspace); point the client at the intended workspace and retry."],"exampleFix":"// before: guessing the old password in a script\nChangeMasterPassword(oldPass, newPass)\n// after: validate the old password by unwrapping a box first\nif err := UnlockEncryptedNotebook(boxID, oldPass); err != nil {\n    return fmt.Errorf(\"old master password incorrect, aborting change: %w\", err)\n}\nreturn ChangeMasterPassword(oldPass, newPass)","handlingStrategy":"validation","validationCode":"// Validate the old master password before changing it\n// (Go, caller side of the kernel API)\nfor _, boxID := range encryptedBoxIDs {\n    if err := model.VerifyNotebookPassword(boxID, oldPassword); err != nil {\n        return fmt.Errorf(\"old master password cannot decrypt box %s: %w\", boxID, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := model.ChangeMasterPassword(old, new); err != nil {\n    if strings.Contains(err.Error(), \"[box=\") {\n        id := err.Error()[strings.Index(err.Error(), \"[box=\")+5 : len(err.Error())-1]\n        // surface which notebook failed and keep the old password active\n    }\n    return err\n}","preventionTips":["Always confirm the old password by unlocking an encrypted notebook before initiating a password change.","Keep per-notebook .siyuan/conf.json out of manual edits and partial backups.","Run SiYuan sync/snapshots so corrupted box confs can be restored from a known-good state.","Avoid restoring boxes across workspaces with different MasterSalt values."],"tags":["encryption","master-password","key-wrapping","data-corruption"],"backgroundTag":"wrapped-dek-decrypt-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}