siyuan-note/siyuan · error
encrypted box db not opened for box {boxID}
Error message
encrypted box db not opened for box {boxID} What it means
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.
Source
Thrown at kernel/sql/stmt_validate.go:181
// 注意:若字符串里在语法上还有第二条及以后的语句,本函数只针对「首条」对应的 stmt 做判断,
// 不会拒绝多语句。与 CheckSingleStatement 组合即可得到「单条 + 只读」策略。
// 仅允许 SELECT 和 WITH 查询,避免 SQLite 将 ATTACH、DETACH 和事务控制语句标记为只读后放行。
func CheckReadonlyStatement(stmt string) error {
return checkReadonlyStatement(stmt, db)
}
// CheckAssetContentReadonlyStatement 在资源文件内容数据库连接上检查 SQL 是否只读。
func CheckAssetContentReadonlyStatement(stmt string) error {
return checkReadonlyStatement(stmt, assetContentDB)
}
// CheckReadonlyStatementInBox 在指定笔记本对应的数据库连接上检查 SQL 是否只读。
func CheckReadonlyStatementInBox(stmt, boxID string) error {
targetDB := db
if boxDB := GetEncryptedDB(boxID); nil != boxDB {
targetDB = boxDB
} else if IsEncryptedBoxFn != nil && IsEncryptedBoxFn(boxID) {
return errors.New("encrypted box db not opened for box " + boxID)
}
return checkReadonlyStatement(stmt, targetDB)
}
func checkReadonlyStatement(stmt string, targetDB *sql.DB) error {
if strings.TrimSpace(stmt) == "" {
return errors.New("SQL statement is empty")
}
if !isReadonlyQueryStatement(stmt) {
return errors.New("SQL statement is not a read-only query")
}
if nil == targetDB {
return errors.New("database is nil")
}
ctx := context.Background()
conn, err := targetDB.Conn(ctx)
if err != nil {
return errView on GitHub (pinned to 251596fc0d)
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).
Example fix
// before: query encrypted box without unlocking
api.QuerySQLBox("SELECT * FROM blocks", encryptedBoxID)
// after: unlock first, then query
api.UnlockBox(encryptedBoxID, passphrase)
api.QuerySQLBox("SELECT * FROM blocks", encryptedBoxID) Defensive patterns
Strategy: validation
Validate before calling
// Caller side: ensure the encrypted box is unlocked before querying.
async function ensureBoxUnlocked(boxID: string, passphrase: string): Promise<void> {
const opened = await api.isEncryptedBoxOpened(boxID)
if (!opened) await api.openEncryptedBox(boxID, passphrase)
} Try / catch
try { return await api.querySQLInBox(stmt, boxID) }
catch (e) {
if (/encrypted box db not opened/i.test(String(e))) {
await api.openEncryptedBox(boxID, passphrase)
return api.querySQLInBox(stmt, boxID)
}
throw e
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- CLI does not support encrypted notebook [%s]
- CLI does not support files in encrypted notebooks
- Encrypted notebooks do not support this operation
- encrypted notebook is locked, please unlock it first
- encrypted notebook is locked, please unlock it first
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/5a06c759568cb763.
Report an issue: GitHub.