siyuan-note/siyuan · error
SQL statement is empty
Error message
SQL statement is empty
What it means
checkReadonlyStatement validates that a SQL statement handed to a read-only query path is a non-empty SELECT before it is prepared against SQLite. The kernel throws this when the statement is empty or contains only whitespace, because preparing an empty statement cannot yield a result set. It is a defensive pre-flight check that guards all read-only statement entry points.
Solutions
- Ensure the SQL string is non-empty before calling any Check*Readonly* function
- Validate user/API input at the endpoint boundary and return a 400 with a clear message instead of forwarding an empty stmt
- Log the raw stmt value at the call site to find where the empty string originates
Example fix
// before
err := sql.CheckReadonlyStatement(stmt)
// after
if strings.TrimSpace(stmt) == "" {
return errors.New("query is empty: provide a SELECT statement")
}
err := sql.CheckReadonlyStatement(stmt) Defensive patterns
Strategy: validation
Validate before calling
function canQuery(stmt) { return typeof stmt === "string" && stmt.trim().length > 0; }
if (!canQuery(stmt)) throw new Error("SQL statement required"); Type guard
function isNonEmptyString(v) { return typeof v === "string" && v.trim().length > 0; } Try / catch
try {
await runQuery(stmt);
} catch (e) {
if (String(e.message) === "SQL statement is empty") showUserError("Provide a SELECT statement");
else throw e;
} Prevention
- Always trim and check SQL strings before submitting to query APIs
- Default-fill query fields in configs so they are never empty
- Log the statement at the call site to catch empty-value regressions early
When it happens
Trigger: Calling CheckReadonlyStatement, CheckAssetContentReadonlyStatement, or CheckReadonlyStatementInBox with an empty string, a string of spaces/tabs/newlines, or an argument that was never assigned (e.g. an empty config field or unmarshalled empty JSON string).
Common situations: An API caller passes an empty 'stmt' parameter; a frontend query builder produces '' when no SQL was composed; a stored query config field is blank after a version migration.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- block [ ] is not a document that can declare a child…
- Conf.Language(106)
- Conf.Language(112)
- Conf.Language(16)
- duplicate source ID [ ]
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/e11065f041786535.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/sql/stmt_validate.go:200
return err
}
return CheckReadonlyStatementInBox(stmt, boxID)
}
// 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 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)View on GitHub (pinned to 9f775e8a12)