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

  1. Read the recovered panic value in the message to locate the panic (usually 'runtime error: ...'); fix the bug in the on_row callback
  2. Check for NULL columns before use: verify with RowMap.IsNull(col) or check .String/.Int64 nil handling for each column accessed
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/85ceb9c4b8e9237f. Report an issue: GitHub.