siyuan-note/siyuan · error
encrypted notebook key envelope creation time is missing
Error message
encrypted notebook key envelope creation time is missing
What it means
Thrown by validateWrappedDEKEnvelope when enc.CreatedAt <= 0. CreatedAt is a Unix-millisecond timestamp set at key-wrap time; it must be positive. A zero or negative value indicates the field was never set, was lost during serialization, or was corrupted.
Source
Thrown at kernel/model/crypto.go:1615
}
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
}
if _, err := util.EncryptionNonce(enc.Metadata); err != nil {
return fmt.Errorf("invalid encrypted notebook metadata envelope: %w", err)
}View on GitHub (pinned to 251596fc0d)
Solutions
- Restore conf.json from a backup that has a valid CreatedAt timestamp.
- Restore the per-notebook crypt backup if it has a valid CreatedAt.
- If you have access to the source code and are migrating notebooks programmatically, ensure CreatedAt is set to time.Now().UnixMilli() when constructing BoxEncryption.
Example fix
// before: BoxEncryption missing CreatedAt
boxConf.BoxCrypt = &conf.BoxEncryption{
WrappedDEK: wrapped,
WrapNonce: nonce,
Spec: boxEncryptionSpec,
// CreatedAt missing
}
// after
boxConf.BoxCrypt = &conf.BoxEncryption{
WrappedDEK: wrapped,
WrapNonce: nonce,
Spec: boxEncryptionSpec,
CreatedAt: time.Now().UnixMilli(),
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the envelope before attempting unlock:
boxCrypt, err := model.GetBoxEncryption(boxID)
if err != nil {
return
}
if boxCrypt.CreatedAt <= 0 {
// timestamp missing — restore conf from backup
return
} Prevention
- Never omit or zero the CreatedAt field when editing conf.json.
- Use SiYuan's API to create/modify encrypted notebooks — it always sets CreatedAt.
- Keep backups of valid conf.json files.
When it happens
Trigger: Reached via decryptWrappedDEK during unlock or ChangeMasterPassword. Fires when the BoxEncryption struct's CreatedAt field is 0 or negative — e.g., the JSON field was omitted during manual editing, a sync conflict dropped it, or the struct was constructed without setting CreatedAt.
Common situations: Manual editing of conf.json removed the CreatedAt field. A sync-conflict resolution script or third-party merge tool dropped the field. A bug in a custom import/migration tool that constructs BoxEncryption without setting CreatedAt.
Related errors
- unsupported encrypted notebook key envelope
- path belongs to encrypted notebook [%s]: %s
- invalid encrypted notebook key envelope: %w
- encrypted notebook key envelope nonce mismatch
- invalid encrypted notebook metadata envelope: %w
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/943b7f35cd761f88.
Report an issue: GitHub.