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

  1. 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.
  2. Clear the cached DEK (lock the notebook) before retrying so prepareBoxConfForSave takes the metadata-passthrough branch instead of re-encrypting.
  3. If the notebook is meant to be unencrypted, disable encryption through the proper flow rather than nil-ing BoxCrypt.
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/0110d93adeadeb61. Report an issue: GitHub.