siyuan-note/siyuan · critical
Conf.Language(316) + " [box=" + id + "]"
Error message
Conf.Language(316) + " [box=" + id + "]"
What it means
During master-password migration Phase 0, each encrypted notebook's BoxCrypt is decrypted with the old KEK via decryptBoxCrypt(). If decryption fails for a box, migration aborts with a localized message (Conf.Language(316): 'Decryption failed: incorrect key or corrupted data') plus the box ID. The migration refuses to proceed because it cannot re-wrap a DEK it cannot decrypt.
Solutions
- Retry the migration with the correct ORIGINAL master password (the one the box was wrapped under)
- Identify the failing box from the [box=ID] suffix and check its .siyuan/conf.json and notebook crypt backup for corruption
- If the notebook data is still accessible, decrypt/unlock it manually and re-encrypt under the current password before migrating
- Restore the affected notebook's conf/backup from a known-good sync or backup snapshot, then retry
Example fix
// before: box wrapped under old KEK blocks migration ChangeMasterPassword(currentPW, newPW) // -> Decryption failed: incorrect key or corrupted data [box=20240101120000-abcd123] // after: unlock/re-encrypt the box with the password it was created under, or restore its backup, then rerun ChangeMasterPassword
Defensive patterns
Strategy: validation
Validate before calling
// Ensure every encrypted box decrypts under the current password before migrating
for _, id := range encBoxIDs {
if _, _, err := decryptBoxCrypt(id, oldKEK); err != nil {
return fmt.Errorf("box %s not decryptable with current password; resolve before migration", id)
}
} Try / catch
if err := model.ChangeMasterPassword(old, new); err != nil {
if strings.Contains(err.Error(), "Decryption failed") && strings.Contains(err.Error(), "[box=") {
boxID := extractBetween(err.Error(), "[box=", "]")
// recover that box: correct password / restore its conf+backup from snapshot
}
} Prevention
- Always perform password changes while logged in with the current master password verified (unlock an encrypted notebook first)
- Avoid mixing notebooks restored from older-password backups into a workspace with a newer password without re-encrypting them
- Take a workspace snapshot before any master-password migration
- Let pending migrations complete before changing the password again
When it happens
Trigger: ChangeMasterPassword iterating encBoxIDs calls decryptBoxCrypt(id, oldKEK); it fails when the notebook's WrappedDEK/backup was encrypted with a different KEK, the stored key material is corrupted, or the old password/KEK supplied does not match the one the box was wrapped under.
Common situations: User changed the master password previously and some notebook still holds key material wrapped under the older KEK; per-notebook conf or backup corrupted by an interrupted write; restoring a notebook from an old backup into a workspace with a newer master password.
Related errors
- decrypt box [ ] failed: incorrect key or corrupted data
- encrypted notebook has no valid key material
- invalid historical notebook encryption key
- master password migration is pending
- no encrypted key material for box
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/e9e302a0eeebe6db.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/crypto.go:1757
if err != nil {
return err
}
newVerifier, err := util.EncryptWithAAD(newKEK, kekVerifierMagic, []byte("siyuan:kek-verifier"))
if err != nil {
return err
}
// Phase 0: 遍历所有加密笔记本(含 conf 损坏但存在备份的),预计算新 WrappedDEK(内存操作)
// 允许 entries 为空:用户可能已启用加密功能但尚未创建加密笔记本,此时仍需更新全局 verifier 和 backup。
encBoxIDs, listErr := listAllEncryptedBoxIDs()
if listErr != nil {
return fmt.Errorf("list encrypted notebooks failed: %w", listErr)
}
var entries []migrationBoxEntry
for _, id := range encBoxIDs {
dek, boxCrypt, dErr := decryptBoxCrypt(id, oldKEK)
if dErr != nil {
return errors.New(Conf.Language(316) + " [box=" + id + "]")
}
newWrapped, nErr := util.EncryptWithAAD(newKEK, dek, wrappedDEKAAD(id))
zeroAndClear(dek)
if nErr != nil {
return nErr
}
entries = append(entries, migrationBoxEntry{
BoxID: id,
NewSpec: boxEncryptionSpec,
NewWrappedDEK: newWrapped,
NewWrapNonce: mustEncryptionNonce(newWrapped),
Metadata: append([]byte(nil), boxCrypt.Metadata...),
})
}
// Phase 1: 持久化 migration manifest(崩溃后 recovery 的依据)
newParamsJSON, _ := gulu.JSON.MarshalJSON(params)
mig := &masterPasswordMigration{View on GitHub (pinned to 9f775e8a12)