siyuan-note/siyuan · error

unsupported encrypted notebook key envelope

Error message

unsupported encrypted notebook key envelope

What it means

Thrown by validateWrappedDEKEnvelope when the BoxEncryption struct is nil or its Spec field does not equal boxEncryptionSpec (currently 1). This is the first structural validation before any decryption is attempted — it rejects envelopes from an incompatible or unknown encryption spec before touching crypto primitives.

Source

Thrown at kernel/model/crypto.go:1612

		WrapNonce:  mustEncryptionNonce(wrapped),
		CreatedAt:  time.Now().UnixMilli(),
	}, dek, nil
}

func wrappedDEKAAD(boxID string) []byte {
	return []byte("siyuan:wrapped-dek:" + boxID)
}

func decryptWrappedDEK(boxID string, enc *conf.BoxEncryption, kek []byte) ([]byte, error) {
	if err := validateWrappedDEKEnvelope(enc); err != nil {
		return nil, err
	}
	return util.DecryptWithAAD(kek, enc.WrappedDEK, wrappedDEKAAD(boxID))
}

func validateWrappedDEKEnvelope(enc *conf.BoxEncryption) error {
	if enc == nil || enc.Spec != boxEncryptionSpec {
		return errors.New("unsupported encrypted notebook key envelope")
	}
	if enc.CreatedAt <= 0 {
		return errors.New("encrypted notebook key envelope creation time is missing")
	}
	nonce, err := util.EncryptionNonce(enc.WrappedDEK)
	if err != nil {
		return fmt.Errorf("invalid encrypted notebook key envelope: %w", err)
	}
	if !bytes.Equal(nonce, enc.WrapNonce) {
		return errors.New("encrypted notebook key envelope nonce mismatch")
	}
	return nil
}

func validateBoxEncryption(enc *conf.BoxEncryption) error {
	if err := validateWrappedDEKEnvelope(enc); err != nil {
		return err
	}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Update SiYuan to the version that created the notebook — a newer spec may be in use.
  2. Restore conf.json from a DataDir backup that has a valid Spec=1 BoxCrypt.
  3. Restore the per-notebook crypt backup if it has the correct Spec.
  4. If the notebook was created on an incompatible version, export the data from the original version and re-import on this version.
Defensive patterns

Strategy: validation

Validate before calling

// Before unlocking, verify the BoxEncryption envelope spec:
boxCrypt, err := model.GetBoxEncryption(boxID)
if err != nil {
    return
}
if boxCrypt.Spec != 1 { // boxEncryptionSpec
    // incompatible spec — update SiYuan or restore a compatible conf
    return
}

Prevention

When it happens

Trigger: Called indirectly via decryptWrappedDEK, which is reached from decryptBoxCrypt → GetBoxEncryption during unlock, or from ChangeMasterPassword Phase 0. Fires when conf.json or the per-notebook backup contains a BoxCrypt with Spec=0 (unset), Spec=2+ (future version), or when BoxCrypt is entirely nil.

Common situations: Downgrading SiYuan to an older version that doesn't understand a newer spec. Manually editing conf.json and losing the Spec field. A third-party tool or sync conflict produced a BoxCrypt JSON object without the Spec field. Importing a notebook from a different or future SiYuan version.

Related errors


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