siyuan-note/siyuan · critical
no encrypted key material for box
Error message
no encrypted key material for box
What it means
unlockBoxHeld rejects the unlock when the supplied boxEnc is nil or has an empty WrappedDEK, marking the box state as Error. Without wrapped key material there is nothing for the KEK to unwrap, so the notebook cannot be decrypted. This is the pre-check counterpart of the decryptBoxCrypt error but raised before any key derivation.
Solutions
- Reload the notebook's BoxEncryption from disk (conf.json) instead of using a stale cached value
- Restore the BoxCrypt section or the notebook crypto backup for this box from a good backup
- Confirm the notebook is actually encrypted before attempting unlock
Example fix
// before
boxEnc := cachedBoxEnc[boxID] // may be nil
model.UnlockBox(boxID, pw, boxEnc)
// after
boxEnc := loadBoxEncryptionFromConf(boxID)
if boxEnc == nil || len(boxEnc.WrappedDEK) == 0 {
return fmt.Errorf("box %s is missing encrypted key material", boxID)
}
model.UnlockBox(boxID, pw, boxEnc) Defensive patterns
Strategy: validation
Validate before calling
if boxEnc == nil || len(boxEnc.WrappedDEK) == 0 {
return errors.New("box is missing wrapped DEK; cannot unlock")
} Type guard
func unlockable(b *conf.BoxEncryption) bool {
return b != nil && len(b.WrappedDEK) > 0
} Prevention
- Always load BoxEncryption fresh from conf, never from long-lived caches
- Restore missing BoxCrypt sections before attempting unlock
- Check notebook encryption status before presenting unlock UI
When it happens
Trigger: UnlockBox/UnlockAndMountBox/ChangeMasterPassword invoked with boxEnc nil, or a BoxEncryption whose WrappedDEK slice is empty — typically because conf.json has no BoxCrypt section for the notebook.
Common situations: Caller cached an old BoxEncryption before the notebook was encrypted; conf.json truncated or manually edited; notebook re-created without encryption metadata; sync dropped the crypto section.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Conf.Language(316) + " [box=" + id + "]"
- decrypt box [ ] failed: incorrect key or corrupted data
- encrypted notebook has no valid key material
- invalid historical notebook encryption key
- no encrypted key material for box
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/ecab7b95d651445b.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/crypto.go:1404
wasUnlocked := IsBoxUnlocked(boxID)
if err = unlockBoxHeld(boxID, password, boxEnc); err != nil {
return false, err
}
alreadyMount, err = mountBox(boxID)
if err != nil && !wasUnlocked {
lockBoxWithPreparationHeld(boxID, nil)
}
return alreadyMount, err
}
func unlockBoxHeld(boxID string, password string, boxEnc *conf.BoxEncryption) (err error) {
if _, busy := boxLock.Load(boxID); busy {
return errors.New(Conf.language(239))
}
if boxEnc == nil || len(boxEnc.WrappedDEK) == 0 {
setEncryptedBoxState(boxID, EncryptedBoxStateError)
return errors.New("no encrypted key material for box")
}
if IsBoxUnlocked(boxID) {
if GetEncryptedBoxState(boxID) == EncryptedBoxStateError {
return errors.New(Conf.Language(316))
}
setEncryptedBoxState(boxID, EncryptedBoxStateUnlocked)
return nil
}
setEncryptedBoxState(boxID, EncryptedBoxStateUnlocking)
// 获取 box 写锁,与 LockBox/unmount0 串行化,防止并发锁/解锁导致 db/DEK 状态不一致
acquireBoxWriteLock(boxID)
finalState := EncryptedBoxStateLocked
defer func() {
releaseBoxWriteLock(boxID)
setEncryptedBoxState(boxID, finalState)
}()
View on GitHub (pinned to 9f775e8a12)