vitessio/vitess · error
QueryRowsMap unexpected error: %+v
Error message
QueryRowsMap unexpected error: %+v
What it means
QueryRowsMap wraps a db.Query loop with a deferred recover. If the on_row callback (or anything else in the function) panics — most commonly a nil pointer dereference or an out-of-range index inside the callback — the panic is converted into this error instead of crashing the process. The %+v captures the recovered panic value, which is usually a runtime error string rather than a real query failure.
Source
Thrown at go/vt/external/golib/sqlutils/sqlutils.go:228
func ScanRowsToMaps(rows *sql.Rows, on_row func(RowMap) error) error {
columns, _ := rows.Columns()
err := ScanRowsToArrays(rows, func(arr []CellData) error {
m := rowToMap(arr, columns)
err := on_row(m)
if err != nil {
return err
}
return nil
})
return err
}
// QueryRowsMap is a convenience function allowing querying a result set while poviding a callback
// function activated per read row.
func QueryRowsMap(db *sql.DB, query string, on_row func(RowMap) error, args ...any) (err error) {
defer func() {
if derr := recover(); derr != nil {
err = fmt.Errorf("QueryRowsMap unexpected error: %+v", derr)
}
}()
var rows *sql.Rows
rows, err = db.Query(query, args...)
if rows != nil {
defer rows.Close()
}
if err != nil && err != sql.ErrNoRows {
log.Error(fmt.Sprint(err))
return err
}
err = ScanRowsToMaps(rows, on_row)
return
}
// ExecNoPrepare executes given query using given args on given DB, without using prepared statements.
func ExecNoPrepare(db *sql.DB, query string, args ...any) (res sql.Result, err error) {View on GitHub (pinned to 01a25a7d17)
Solutions
- Read the recovered panic value in the message to locate the panic (usually 'runtime error: ...'); fix the bug in the on_row callback
- Check for NULL columns before use: verify with RowMap.IsNull(col) or check .String/.Int64 nil handling for each column accessed
- Validate column names/indices against the actual query result set before accessing them in the callback
Example fix
// before: panics on NULL/missing column
err := sqlutils.QueryRowsMap(db, q, func(r sqlutils.RowMap) error {
name := r["username"].String()
...
})
// after: check column existence/nullness
err := sqlutils.QueryRowsMap(db, q, func(r sqlutils.RowMap) error {
var name string
if !r.IsNull("username") {
name = r["username"].String()
}
...
}) Defensive patterns
Strategy: try-catch
Validate before calling
func rowMapSafe(r sqlutils.RowMap, col string) (sqlutils.Value, bool) {
v, ok := r[col]
return v, ok
} Try / catch
// QueryRowsMap already recovers panics into err; handle it at the call site
if err := sqlutils.QueryRowsMap(db, q, onRow); err != nil {
if strings.Contains(err.Error(), "unexpected error") {
log.Errorf("bug in onRow callback: %v", err)
}
return err
} Prevention
- Check row.IsNull(col) or presence of the column before accessing RowMap values
- Never assume column names exist — derive them from the query or check dynamically
- Keep on_row callbacks small and nil-safe
When it happens
Trigger: Calling sqlutils.QueryRowsMap with an on_row callback that panics (nil map/pointer access, index out of range, calling methods on nil RowMap values); a panic inside db.Query driver code is also caught here.
Common situations: Rows with NULL columns accessed without checking row.IsNull / nil; assuming a column exists in the result set; bugs in custom scanning logic inside the callback.
Related errors
- ExecNoPrepare unexpected error: %+v
- panic: %v
- --batch-size only allowed when all queries are CREATE TABLE|
- this command panics on purpose
- query not supported by vexec
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/85ceb9c4b8e9237f.
Report an issue: GitHub.