siyuan-note/siyuan · error
encrypted notebook key material is missing
Error message
encrypted notebook key material is missing
What it means
In encryptBoxMetadata (box_conf_crypto.go:35), if boxConf == nil or boxConf.BoxCrypt == nil it returns errors.New('encrypted notebook key material is missing'). encryptBoxMetadata is reached from reuseBoxMetadataIfUnchanged, which only calls it when a DEK is cached (notebook unlocked) but the on-disk encrypted metadata either does not exist or the icon/sort changed so a re-encrypt is needed. The guard therefore fires when the in-memory conf claims encryption is active (a DEK is present) yet the BoxCrypt key material that would hold the ciphertext blob is nil — an inconsistent state.
Source
Thrown at kernel/model/box_conf_crypto.go:37
import (
"errors"
"path/filepath"
"github.com/88250/gulu"
"github.com/siyuan-note/filelock"
"github.com/siyuan-note/siyuan/kernel/conf"
"github.com/siyuan-note/siyuan/kernel/util"
)
type encryptedBoxMetadata struct {
Icon string `json:"icon"`
Sort int `json:"sort"`
SortMode int `json:"sortMode"`
}
func encryptBoxMetadata(boxID string, boxConf *conf.BoxConf, dek []byte) error {
if boxConf == nil || boxConf.BoxCrypt == nil {
return errors.New("encrypted notebook key material is missing")
}
metadata := &encryptedBoxMetadata{
Icon: filterBoxIcon(boxConf.Icon),
Sort: boxConf.Sort,
SortMode: boxConf.SortMode,
}
plaintext, err := gulu.JSON.MarshalJSON(metadata)
if err != nil {
return err
}
key := util.DeriveSubKey(dek, "siyuan/box-metadata")
defer zeroAndClear(key)
boxConf.BoxCrypt.Metadata, err = util.EncryptWithAAD(key, plaintext, boxMetadataAAD(boxID))
return err
}
func decryptBoxMetadata(boxID string, boxConf *conf.BoxConf, dek []byte) error {
if boxConf == nil || boxConf.BoxCrypt == nil || len(boxConf.BoxCrypt.Metadata) == 0 {View on GitHub (pinned to 251596fc0d)
Solutions
- Do not hand-edit an encrypted notebook's conf.json — re-establish encryption via the UI/CLI so BoxCrypt and the DEK are set together.
- Clear the cached DEK (lock the notebook) before retrying so prepareBoxConfForSave takes the metadata-passthrough branch instead of re-encrypting.
- If the notebook is meant to be unencrypted, disable encryption through the proper flow rather than nil-ing BoxCrypt.
- Restore conf.json from backup if BoxCrypt was lost, then unlock normally.
Example fix
// before: nil-ing BoxCrypt while leaving Encrypted true
boxConf.Encrypted = true
boxConf.BoxCrypt = nil
box.SaveConf(boxConf) // -> key material is missing
// after: toggle encryption only through the dedicated flow so key material stays consistent
if err := model.SetBoxEncryption(boxID, passphrase, enable); err != nil {
return err
} Defensive patterns
Strategy: validation
Validate before calling
// Refuse to save an encrypted notebook that lost its key material.
if conf.Encrypted && conf.BoxCrypt == nil {
return errors.New("cannot save encrypted notebook without key material")
} Try / catch
// On this error, lock the notebook (clear the stale DEK) and retry via the proper encryption flow.
if err := box.SaveConf(conf); err != nil && err.Error() == "encrypted notebook key material is missing" {
lockNotebook(box.ID)
err = reenableEncryption(box.ID, passphrase)
} Prevention
- Never hand-edit an encrypted notebook's conf.json or nil out BoxCrypt.
- Change encryption state only through the dedicated enable/disable flow.
- If a migration reshapes BoxCrypt, clear cached DEKs so the two cannot drift.
When it happens
Trigger: Box.SaveConf on an unlocked encrypted notebook whose BoxCrypt was cleared/nil'd while a DEK remained cached and the persisted metadata needed (re-)encryption: e.g. a buggy migration that dropped BoxCrypt, a manual edit of conf.json that removed the BoxCrypt object but left Encrypted=true, or a code path that re-armed encryption without re-attaching key material.
Common situations: Editing conf.json by hand and stripping BoxCrypt; a partial encryption-enable/disable flow that nil'd the field mid-operation; version upgrade that reshaped BoxCrypt and a stale cached DEK outlived the struct change.
Related errors
- encrypted notebook is locked, please unlock it first
- CLI does not support encrypted notebook [%s]
- CLI does not support files in encrypted notebooks
- path belongs to encrypted notebook [%s]: %s
- accessing assets in encrypted notebook [%s] is not supported
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/0110d93adeadeb61.
Report an issue: GitHub.