t8y2/dbx · error
table read requires a SELECT query
Error message
table read requires a SELECT query
What it means
The Oracle driver's table-read path only executes SELECT statements, since it pages through a live server-side cursor (runPagedOracleSelect). startTableRead validates the trimmed SQL with isQuerySQL and refuses anything that is not a query. This protects the paged-read machinery, which cannot handle DML/DDL that returns no row set.
Source
Thrown at agents/drivers/oracle-go/main.go:3874
}
func (s *server) storeQuerySession(session *querySession) string {
s.nextSessionID++
sessionID := fmt.Sprintf("oracle-go-%d", s.nextSessionID)
s.sessions[sessionID] = session
return sessionID
}
func (s *server) startTableRead(opts queryOptions, pageSize int) (queryPageResult, error) {
start := time.Now()
if strings.TrimSpace(opts.Schema) != "" && !s.hasManualTransaction() {
if err := s.setSchema(opts.Schema); err != nil {
return queryPageResult{}, err
}
}
sqlText := trimStatementSQL(opts.SQL)
if !isQuerySQL(sqlText) {
return queryPageResult{}, errors.New("table read requires a SELECT query")
}
result, session, err := s.runPagedOracleSelect(sqlText, opts, pageSize, start)
if err != nil {
return queryPageResult{}, err
}
if session != nil {
sessionID := s.storeTableReadSession(session)
result.SessionID = &sessionID
}
return result, nil
}
func (s *server) fetchTableReadPage(sessionID string, pageSize int) (queryPageResult, error) {
session := s.tableReadSessions[sessionID]
if session == nil {
return queryPageResult{Columns: []string{}, ColumnTypes: []string{}, Rows: [][]any{}, SessionID: nil, HasMore: false}, nil
}
result, err := readQuerySessionPage(session, pageSize)View on GitHub (pinned to c0390bff16)
Solutions
- Rewrite the statement as a SELECT (the table-read API is read-only paging over a cursor).
- Run DML/DDL through the driver's generic execute/non-query API instead of the table-read path.
- Ensure the SQL literally starts with a SELECT clause recognized by isQuerySQL and contains no leading comments/whitespace tricks that hide it.
Example fix
// before
res, err := client.TableRead(ctx, QueryOptions{SQL: "DELETE FROM orders WHERE id = 7"})
// after
res, err := client.Execute(ctx, "DELETE FROM orders WHERE id = 7") // non-query API
// or for reads:
res, err := client.TableRead(ctx, QueryOptions{SQL: "SELECT * FROM orders WHERE id = 7"}) Defensive patterns
Strategy: validation
Validate before calling
sqlText := strings.TrimSpace(opts.SQL)
if !strings.HasPrefix(strings.ToUpper(sqlText), "SELECT") {
return fmt.Errorf("table read needs SELECT, got: %.40s", sqlText)
} Type guard
func isSelectQuery(sql string) bool {
s := strings.TrimSpace(strings.TrimLeft(strings.TrimSpace(sql), "(--/* \t\n"))
return strings.HasPrefix(strings.ToUpper(s), "SELECT")
} Prevention
- Route DML/DDL through the execute API, never the table-read API.
- Sanitize/normalize SQL (strip comments) before the SELECT check.
- Write a unit test asserting the paged-read path rejects non-SELECT statements.
When it happens
Trigger: Calling the table-read/paged-query API with opts.SQL set to an INSERT, UPDATE, DELETE, MERGE, DDL statement (CREATE/ALTER/DROP), or an empty/whitespace string, or a SELECT-less statement that the isQuerySQL heuristic does not recognize as a query.
Common situations: Developers reusing a generic 'run SQL' wrapper and pointing it at the table-read endpoint; passing a script with trailing comments that defeats the SELECT-prefix heuristic; trying to mutate data through a read-oriented paging API.
Related errors
- Query timeout cannot be negative: " + timeoutSecs
- SQL is required
- SQL is required
- SQL is required
- sql is required
AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05).
Data as JSON: /api/errors/72ebc1053a90f2f4.
Report an issue: GitHub.