{"record":{"id":"0110d93adeadeb61","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-key-material-is-missing","errorCode":null,"errorMessage":"encrypted notebook key material is missing","messagePattern":"encrypted notebook key material is missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/model/box_conf_crypto.go","lineNumber":37,"sourceCode":"import (\n\t\"errors\"\n\t\"path/filepath\"\n\n\t\"github.com/88250/gulu\"\n\t\"github.com/siyuan-note/filelock\"\n\t\"github.com/siyuan-note/siyuan/kernel/conf\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\ntype encryptedBoxMetadata struct {\n\tIcon     string `json:\"icon\"`\n\tSort     int    `json:\"sort\"`\n\tSortMode int    `json:\"sortMode\"`\n}\n\nfunc encryptBoxMetadata(boxID string, boxConf *conf.BoxConf, dek []byte) error {\n\tif boxConf == nil || boxConf.BoxCrypt == nil {\n\t\treturn errors.New(\"encrypted notebook key material is missing\")\n\t}\n\tmetadata := &encryptedBoxMetadata{\n\t\tIcon:     filterBoxIcon(boxConf.Icon),\n\t\tSort:     boxConf.Sort,\n\t\tSortMode: boxConf.SortMode,\n\t}\n\tplaintext, err := gulu.JSON.MarshalJSON(metadata)\n\tif err != nil {\n\t\treturn err\n\t}\n\tkey := util.DeriveSubKey(dek, \"siyuan/box-metadata\")\n\tdefer zeroAndClear(key)\n\tboxConf.BoxCrypt.Metadata, err = util.EncryptWithAAD(key, plaintext, boxMetadataAAD(boxID))\n\treturn err\n}\n\nfunc decryptBoxMetadata(boxID string, boxConf *conf.BoxConf, dek []byte) error {\n\tif boxConf == nil || boxConf.BoxCrypt == nil || len(boxConf.BoxCrypt.Metadata) == 0 {","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/box_conf_crypto.go#L19-L55","documentation":"encryptBoxMetadata seals an encrypted notebook's metadata (icon, sort, sortMode) using the notebook's key material stored in boxConf.BoxCrypt. This error means the BoxConf passed in is nil or has no BoxCrypt section, so there is no DEK-derived key material available to encrypt the metadata — an invariant required for encrypted notebooks.","triggerScenarios":"Calling encryptBoxMetadata (via reuseBoxMetadataIfUnchanged or createEncryptedBox, exercised in encrypted-replay tests) with a BoxConf lacking BoxCrypt, e.g. creating an encrypted notebook without completing key setup, or loading a conf where BoxCrypt was stripped/corrupted.","commonSituations":"Interrupted encrypted-notebook creation leaving conf.json without BoxCrypt; manually edited conf.json removing the boxCrypt field; copying a plaintext conf into an encrypted notebook; tests constructing BoxConf without key material.","solutions":["Recreate the encrypted notebook through the proper creation flow so BoxCrypt (key material) is initialized","Restore the boxCrypt section of <DataDir>/<boxID>/.siyuan/conf.json from a valid backup or the notebook crypto backup","Ensure the DEK is unlocked/cached before operations that encrypt metadata","Guard callers: check conf.BoxCrypt != nil before invoking encryption paths"],"exampleFix":"// before\nerr := encryptBoxMetadata(boxID, conf, dek) // conf.BoxCrypt == nil\n// after\nif conf == nil || conf.BoxCrypt == nil {\n    conf.BoxCrypt = initBoxCrypt(boxID, dek) // initialize key material first\n}\nerr := encryptBoxMetadata(boxID, conf, dek)","handlingStrategy":"type-guard","validationCode":"// Go: verify key material exists before encryption paths\nif conf == nil || conf.BoxCrypt == nil {\n    return errors.New(\"notebook crypt key material not initialized\")\n}","typeGuard":"func hasBoxCrypt(c *conf.BoxConf) bool {\n    return c != nil && c.BoxCrypt != nil\n}","tryCatchPattern":"if err := encryptBoxMetadata(boxID, conf, dek); err != nil {\n    if strings.Contains(err.Error(), \"key material is missing\") {\n        // re-run encrypted notebook setup / restore boxCrypt from backup\n    }\n    return err\n}","preventionTips":["Create encrypted notebooks only via the standard creation flow","Do not hand-edit conf.json or remove the boxCrypt field","Unlock the notebook (DEK cached) before metadata writes","Keep the notebook crypto backup file intact for recovery"],"tags":["go","encryption","notebook","key-material"],"backgroundTag":"missing-credentials","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}