{"record":{"id":"04ed48bfcee3beab","repo":"siyuan-note/siyuan","slug":"encrypted-notebook-metadata-verification-failed-af","errorCode":null,"errorMessage":"encrypted notebook metadata verification failed after write","messagePattern":"encrypted notebook metadata verification failed after write","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"kernel/model/crypto.go","lineNumber":2666,"sourceCode":"\t}\n\n\tbox := &Box{ID: id}\n\tboxConf := box.GetConf()\n\tboxConf.Encrypted = true\n\tboxConf.BoxCrypt = enc\n\tif err = encryptBoxMetadata(id, boxConf, dek); err != nil {\n\t\treturn \"\", fmt.Errorf(\"encrypt notebook metadata failed: %w\", err)\n\t}\n\tif err = box.SaveConf(boxConf); err != nil {\n\t\treturn \"\", fmt.Errorf(\"save encrypted notebook conf failed: %w\", err)\n\t}\n\tif err = writeNotebookCryptBackup(id, enc); err != nil {\n\t\treturn \"\", fmt.Errorf(\"write notebook crypt backup failed: %w\", err)\n\t}\n\t// 回读校验加密配置已落盘，避免写失败后按普通笔记本处理\n\tverifyConf := box.GetConf()\n\tif verifyConf == nil || !verifyConf.Encrypted || verifyConf.BoxCrypt == nil {\n\t\terr = errors.New(\"encrypted notebook metadata verification failed after write\")\n\t\treturn \"\", err\n\t}\n\tmarkRuntimeEncryptedBox(id)\n\tinvalidateEncryptedPublishAccessCache()\n\n\t// 复用刚派生的 DEK 直接开 db + 缓存，省去再次 Argon2id 解锁\n\tcachedDEKsLock.Lock()\n\tif err = sql.OpenEncryptedDB(id, dek); err != nil {\n\t\tcachedDEKsLock.Unlock()\n\t\treturn \"\", err\n\t}\n\tif err = treenode.OpenEncryptedBlockTreeDB(id, dek); err != nil {\n\t\tsql.CloseEncryptedDB(id)\n\t\tcachedDEKsLock.Unlock()\n\t\treturn \"\", err\n\t}\n\tcachedDEKs[id] = dek\n\tcachedDEKsLock.Unlock()","sourceCodeStart":2648,"sourceCodeEnd":2684,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/crypto.go#L2648-L2684","documentation":"After writing all encrypted state (metadata, conf, backup), the code reads the box conf back and verifies Encrypted is true and BoxCrypt is present. If the read-back does not confirm the state persisted, it raises this explicit error instead of silently treating the notebook as encrypted. It is an internal invariant check guarding against silent write failures (e.g. cache staleness or a failed save that reports success).","triggerScenarios":"box.GetConf() right after SaveConf returns nil, nil, or a conf with Encrypted=false / BoxCrypt=nil — i.e. the on-disk or cached configuration does not reflect the just-written encrypted state.","commonSituations":"Filesystem caching or external sync restoring conf.json between write and read-back; a bug in SaveConf silently no-op; concurrent modification of conf.json by another kernel instance or process during conversion.","solutions":["Re-run the enable-encryption operation; a transient read-back race usually resolves on retry","Check for concurrent access: only one kernel instance should run against the workspace; stop sync clients during conversion","Inspect data/<box>/conf.json manually — if it lacks the encrypted fields, re-attempt the conversion after fixing the storage layer","If reproducible, report as a bug with kernel logs (this path signals an internal invariant violation)"],"exampleFix":"// before (external process restoring old conf mid-conversion)\nsuspend Dropbox/OneDrive sync on the workspace\n// after\nretry enable-encryption with sync paused -> verification passes","handlingStrategy":"try-catch","validationCode":"// ensure single kernel instance and no external writers before converting\nconst conf = await fetchGet(\"/api/system/version\"); // kernel reachable, single instance assumed\npauseSyncClients();","typeGuard":null,"tryCatchPattern":"try {\n    await enableNotebookEncryption(boxID, password);\n} catch (e) {\n    if (String(e.message).includes(\"verification failed after write\")) {\n        stopExternalWriters();\n        await enableNotebookEncryption(boxID, password); // retry once\n        if (stillFailing) reportBugWithKernelLogs();\n    }\n}","preventionTips":["Run exactly one kernel instance per workspace","Pause external sync/restore tools during encryption setup","After conversion, confirm the notebook shows as encrypted in the UI before adding data"],"tags":["invariant","consistency","encrypted-notebook"],"backgroundTag":"internal-invariant-violation","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}