siyuan-note/siyuan · error
database is nil
Error message
database is nil
What it means
CheckReadonlyStatement validates that a SQL statement is safe to run and that a target database handle was supplied. After the statement passes emptiness and read-only checks, it verifies the *sql.DB pointer is non-nil. A nil handle means the caller asked to validate/execute against a database that was never opened or has been closed.
Source
Thrown at kernel/sql/stmt_validate.go:194
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 err
}
defer conn.Close()
return conn.Raw(func(dc any) error {
sqliteConn, ok := dc.(*sqlite3.SQLiteConn)
if !ok {
return fmt.Errorf("SQL driver connection type is unexpected: %T", dc)
}
ds, err := sqliteConn.Prepare(stmt)
if err != nil {
return err
}
defer ds.Close()View on GitHub (pinned to 8641553a1f)
Solutions
- Ensure the workspace database is initialized (sql.InitDatabase / database open) before calling CheckReadonlyStatement.
- Check that the correct DB handle (global db, asset-content db, or box db) is passed, not nil.
- If a box was expected, verify the box is not encrypted-locked and its DB was opened via the encrypted-db path.
Example fix
// before
if err := sql.CheckReadonlyStatement(nil, stmt); err != nil { ... }
// after
targetDB := sql.GetDB()
if targetDB == nil {
return errors.New("database not initialized")
}
if err := sql.CheckReadonlyStatement(targetDB, stmt); err != nil { ... } Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof targetDB === 'undefined' || targetDB === null) { throw new Error('target database handle is not initialized') } Type guard
function hasDB(db) { return db != null && typeof db.query === 'function' } Try / catch
try { checkReadonly(db, stmt) } catch (e) { if (String(e.message).includes('database is nil')) { await initDatabase(); retry() } else { throw e } } Prevention
- Always initialize/open the database before running SQL validation
- Centralize DB handle retrieval in one accessor that lazy-initializes
- In tests, assert the DB handle is non-nil in setup
When it happens
Trigger: Calling CheckReadonlyStatement, CheckAssetContentReadonlyStatement, or CheckReadonlyStatementInBox with targetDB == nil — typically when sql.InitDatabase has not run yet, the database was closed, or a nil box/asset-content DB handle was passed in.
Common situations: Kernel startup ordering issues (query before InitDatabase), calling the /api/query/sql API before workspace init, or tests that construct the validator without opening a DB.
Related errors
- encrypted box db not opened for box %s
- encrypted box db not opened for box %s
- get block failed: %s
- attribute view not found
- invalid attribute view id
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/15b07efb41947662.
Report an issue: GitHub.