{"record":{"id":"d0182522d76487cf","repo":"siyuan-note/siyuan","slug":"no-encrypted-key-material-for-box-s","errorCode":null,"errorMessage":"no encrypted key material for box [%s]","messagePattern":"no encrypted key material for box \\[(.+?)\\]","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/model/crypto.go","lineNumber":1324,"sourceCode":"\t\t\treturn nil, errMasterPasswordMigrationPending\n\t\t}\n\t\tif err = saveNotebookCryptoBackup(kek); err != nil {\n\t\t\tzeroAndClear(kek)\n\t\t\treturn nil, fmt.Errorf(\"%w: %v\", errMasterPasswordMigrationPending, err)\n\t\t}\n\t\tremoveMasterPasswordMigration()\n\t}\n\treturn kek, nil\n}\n\n// decryptBoxCrypt 用 KEK 解密 box 的 WrappedDEK。优先使用 GetBoxEncryption 的结果（conf → backup fallback），\n// 若解密失败则尝试 backup 中不同的 WrappedDEK。\n// 返回解密后的 DEK 和实际使用的 BoxCrypt（可能来自 backup）。\n// 若 backup 被使用会自动修复 conf.json 和刷新 backup。\nfunc decryptBoxCrypt(boxID string, kek []byte) (dek []byte, boxCrypt *conf.BoxEncryption, err error) {\n\tboxCrypt, err = GetBoxEncryption(boxID)\n\tif err != nil || boxCrypt == nil || len(boxCrypt.WrappedDEK) == 0 {\n\t\treturn nil, nil, fmt.Errorf(\"no encrypted key material for box [%s]\", boxID)\n\t}\n\n\tnc := currentNotebookCrypto()\n\tdek, err = decryptWrappedDEKWithHistory(boxID, boxCrypt, kek, nc)\n\tif err == nil {\n\t\treturn dek, boxCrypt, nil\n\t}\n\n\t// 主 BoxCrypt 无法解密：尝试 backup 中不同的 WrappedDEK\n\tbackup, bErr := readNotebookCryptBackup(boxID)\n\tif bErr == nil && backup != nil && len(backup.WrappedDEK) > 0 &&\n\t\t!bytes.Equal(backup.WrappedDEK, boxCrypt.WrappedDEK) {\n\t\tdek, err = decryptWrappedDEKWithHistory(boxID, backup, kek, nc)\n\t\tif err == nil {\n\t\t\t// backup 解密成功：修复 conf + 刷新 backup\n\t\t\tbox := &Box{ID: boxID}\n\t\t\tboxConf := box.GetConf()\n\t\t\tboxConf.Encrypted = true","sourceCodeStart":1306,"sourceCodeEnd":1342,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/crypto.go#L1306-L1342","documentation":"decryptBoxCrypt returns this error when the notebook's BoxEncryption record cannot be loaded, is nil, or has an empty WrappedDEK, meaning there is no encrypted key material to unwrap the notebook's DEK. Without a WrappedDEK the notebook cannot be decrypted with any password. It guards against attempting decryption on a notebook whose crypto configuration is missing or corrupt.","triggerScenarios":"decryptBoxCrypt(boxID, kek) is called by unlockBoxHeld (via UnlockBox/UnlockAndMountBox), ChangeMasterPassword, or tests when GetBoxEncryption fails, or conf.json lacks BoxCrypt.WrappedDEK for the given notebook ID.","commonSituations":"Encrypted notebook's conf.json was edited/truncated or lost its BoxCrypt section; notebook was re-created or synced without its crypto config; callers passing a boxID of a non-encrypted notebook; backup file missing while conf is damaged.","solutions":["Check the notebook's conf.json contains an Encrypted flag and BoxCrypt.WrappedDEK; restore it from a backup if missing","Verify the notebook crypto backup file exists and holds a WrappedDEK for this box so the auto-repair path can work","Confirm you are unlocking a notebook that was actually encrypted; non-encrypted notebooks have no key material","Re-sync the workspace from a peer that still has intact key material"],"exampleFix":"// before\nmodel.UnlockBox(boxID, password, boxEnc) // boxEnc loaded from stale/partial conf\n// after\nif boxEnc == nil || len(boxEnc.WrappedDEK) == 0 {\n    return fmt.Errorf(\"box %s has no key material; restore conf.json or decrypt is impossible\", boxID)\n}\nmodel.UnlockBox(boxID, password, boxEnc)","handlingStrategy":"validation","validationCode":"boxEnc, err := model.GetBoxEncryption(boxID)\nif err != nil || boxEnc == nil || len(boxEnc.WrappedDEK) == 0 {\n    return fmt.Errorf(\"box %s has no encrypted key material; refusing unlock\", boxID)\n}","typeGuard":"func hasKeyMaterial(b *conf.BoxEncryption) bool {\n    return b != nil && len(b.WrappedDEK) > 0\n}","tryCatchPattern":null,"preventionTips":["Never hand-edit conf.json of encrypted notebooks","Back up conf.json and the notebook crypto backup together","Check Encrypted flag before treating a notebook as encrypted","Verify sync completeness for crypto files"],"tags":["encryption","key-management","data-loss","go"],"backgroundTag":"missing-required-config-field","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}