{"record":{"id":"56587ce0e08c5fa6","repo":"siyuan-note/siyuan","slug":"encrypted-box-db-not-opened-for-box-s-56587c","errorCode":null,"errorMessage":"encrypted box db not opened for box %s","messagePattern":"encrypted box db not opened for box (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/sql/database.go","lineNumber":1529,"sourceCode":"\t}\n\tif nil == db {\n\t\treturn nil\n\t}\n\treturn db.QueryRow(query, args...)\n}\n\n// queryForBox 按 box 路由查询多行。加密笔记本用独立 db，否则用全局 db。boxID 为空走全局。\n// 加密笔记本未解锁时返回错误——绝不回退全局库。\nfunc queryForBox(boxID, query string, args ...any) (*sql.Rows, error) {\n\tquery = strings.TrimSpace(query)\n\tif \"\" == query {\n\t\treturn nil, errors.New(\"statement is empty\")\n\t}\n\tif boxDB := GetEncryptedDB(boxID); boxDB != nil {\n\t\treturn boxDB.Query(query, args...)\n\t}\n\tif IsEncryptedBoxFn != nil && IsEncryptedBoxFn(boxID) {\n\t\treturn nil, errors.New(\"encrypted box db not opened for box \" + boxID)\n\t}\n\tif nil == db {\n\t\treturn nil, errors.New(\"database is nil\")\n\t}\n\treturn db.Query(query, args...)\n}\n\nfunc queryForBoxContext(ctx context.Context, boxID, query string, args ...any) (*sql.Rows, error) {\n\tquery = strings.TrimSpace(query)\n\tif \"\" == query {\n\t\treturn nil, errors.New(\"statement is empty\")\n\t}\n\tif boxDB := GetEncryptedDB(boxID); boxDB != nil {\n\t\treturn boxDB.QueryContext(ctx, query, args...)\n\t}\n\tif IsEncryptedBoxFn != nil && IsEncryptedBoxFn(boxID) {\n\t\treturn nil, errors.New(\"encrypted box db not opened for box \" + boxID)\n\t}","sourceCodeStart":1511,"sourceCodeEnd":1547,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/sql/database.go#L1511-L1547","documentation":"queryForBox routes box-scoped queries to the box's own database when the box is encrypted. If the box is marked encrypted (IsEncryptedBoxFn) but its database was never opened (GetEncryptedDB returns nil), the function fails closed rather than falling back to the global database, because that would leak or miss encrypted content.","triggerScenarios":"Calling QueryEmptyContentEmbedBlocksInBox, QueryRootBlockByConditionInBox, or the name/alias/title/reftext query helpers with a boxID whose encrypted DB was not opened — e.g. encrypted notebook not unlocked, key not entered, or box opened before the encrypted DB was registered.","commonSituations":"Accessing an encrypted notebook after app restart before entering the access key; embedding-block queries and backlink/ref-text indexing racing ahead of encrypted-db open; API calls targeting a locked encrypted box.","solutions":["Unlock the encrypted notebook (enter the access key) so its per-box DB is opened before querying.","Verify GetEncryptedDB(boxID) is non-nil for this box before issuing box queries.","Check the ordering of box open/encrypted-DB initialization vs the query call in your code path."],"exampleFix":"// before\nblocks := sql.QueryEmptyContentEmbedBlocksInBox(boxID, \"\") // fails if locked\n// after\nif sql.IsEncryptedBox(boxID) && sql.GetEncryptedDB(boxID) == nil {\n    return fmt.Errorf(\"box %s is locked; unlock the encrypted notebook first\", boxID)\n}\nblocks := sql.QueryEmptyContentEmbedBlocksInBox(boxID, \"\")","handlingStrategy":"type-guard","validationCode":"if (isEncryptedBox(boxID) && getEncryptedDB(boxID) == null) { throw new Error('encrypted box locked: ' + boxID) }","typeGuard":"function boxQueryable(boxID) { return !isEncryptedBox(boxID) || getEncryptedDB(boxID) != null }","tryCatchPattern":"try { rows = queryForBox(boxID, sql) } catch (e) { if (String(e.message).startsWith('encrypted box db not opened')) { await unlockEncryptedBox(boxID); rows = queryForBox(boxID, sql) } else { throw e } }","preventionTips":["Unlock encrypted notebooks before running queries or indexing against them","Queue box-scoped background jobs until encrypted DBs are open","Check GetEncryptedDB(boxID) before box-scoped calls"],"tags":["database","encryption","sql","state"],"backgroundTag":"database-query-failed","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}