{"record":{"id":"5a06c759568cb763","repo":"siyuan-note/siyuan","slug":"encrypted-box-db-not-opened-for-box-boxid","errorCode":null,"errorMessage":"encrypted box db not opened for box {boxID}","messagePattern":"encrypted box db not opened for box (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/sql/stmt_validate.go","lineNumber":181,"sourceCode":"// 注意：若字符串里在语法上还有第二条及以后的语句，本函数只针对「首条」对应的 stmt 做判断，\n// 不会拒绝多语句。与 CheckSingleStatement 组合即可得到「单条 + 只读」策略。\n// 仅允许 SELECT 和 WITH 查询，避免 SQLite 将 ATTACH、DETACH 和事务控制语句标记为只读后放行。\nfunc CheckReadonlyStatement(stmt string) error {\n\treturn checkReadonlyStatement(stmt, db)\n}\n\n// CheckAssetContentReadonlyStatement 在资源文件内容数据库连接上检查 SQL 是否只读。\nfunc CheckAssetContentReadonlyStatement(stmt string) error {\n\treturn checkReadonlyStatement(stmt, assetContentDB)\n}\n\n// CheckReadonlyStatementInBox 在指定笔记本对应的数据库连接上检查 SQL 是否只读。\nfunc CheckReadonlyStatementInBox(stmt, boxID string) error {\n\ttargetDB := db\n\tif boxDB := GetEncryptedDB(boxID); nil != boxDB {\n\t\ttargetDB = boxDB\n\t} else if IsEncryptedBoxFn != nil && IsEncryptedBoxFn(boxID) {\n\t\treturn errors.New(\"encrypted box db not opened for box \" + boxID)\n\t}\n\treturn checkReadonlyStatement(stmt, targetDB)\n}\n\nfunc checkReadonlyStatement(stmt string, targetDB *sql.DB) error {\n\tif strings.TrimSpace(stmt) == \"\" {\n\t\treturn errors.New(\"SQL statement is empty\")\n\t}\n\tif !isReadonlyQueryStatement(stmt) {\n\t\treturn errors.New(\"SQL statement is not a read-only query\")\n\t}\n\tif nil == targetDB {\n\t\treturn errors.New(\"database is nil\")\n\t}\n\tctx := context.Background()\n\tconn, err := targetDB.Conn(ctx)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/sql/stmt_validate.go#L163-L199","documentation":"CheckReadonlyStatementInBox returns this when IsEncryptedBoxFn reports the notebook is encrypted but GetEncryptedDB(boxID) returned nil — meaning the encrypted notebook's database has not been unlocked/opened yet. The kernel refuses to run SQL against an encrypted box whose key is not loaded.","triggerScenarios":"Calling the per-box SQL query API with a boxID whose notebook is encrypted but the user has not unlocked it (no password provided / box not yet opened in this session).","commonSituations":"Encrypted notebook exists but kernel just started and user hasn't entered the passphrase; passphrase rejected so the DB stayed closed; automation/script targets an encrypted box without performing unlock first.","solutions":["Unlock the encrypted notebook in the UI (enter its passphrase) before querying, so GetEncryptedDB returns a live connection.","If scripting, perform the unlock step (set box password / open box) before issuing the SQL query.","Verify the boxID is correct and that the box is actually encrypted (otherwise a wrong ID can mismatch)."],"exampleFix":"// before: query encrypted box without unlocking\napi.QuerySQLBox(\"SELECT * FROM blocks\", encryptedBoxID)\n// after: unlock first, then query\napi.UnlockBox(encryptedBoxID, passphrase)\napi.QuerySQLBox(\"SELECT * FROM blocks\", encryptedBoxID)","handlingStrategy":"validation","validationCode":"// Caller side: ensure the encrypted box is unlocked before querying.\nasync function ensureBoxUnlocked(boxID: string, passphrase: string): Promise<void> {\n  const opened = await api.isEncryptedBoxOpened(boxID)\n  if (!opened) await api.openEncryptedBox(boxID, passphrase)\n}","typeGuard":null,"tryCatchPattern":"try { return await api.querySQLInBox(stmt, boxID) }\ncatch (e) {\n  if (/encrypted box db not opened/i.test(String(e))) {\n    await api.openEncryptedBox(boxID, passphrase)\n    return api.querySQLInBox(stmt, boxID)\n  }\n  throw e\n}","preventionTips":["Unlock encrypted notebooks before scripting SQL against them.","Check box open-state before issuing per-box queries.","Confirm the boxID actually refers to an encrypted notebook."],"tags":["sql","sqlite","encryption","notebook","access-control"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}