jmoiron/sqlx · error
expected %s but got %s
Error message
expected %s but got %s
What it means
structOnlyError reports why StructScan rejected a destination: the destination type is not a struct at all (it's a primitive, map, slice, etc.). StructScan only maps rows onto struct types, so any other kind is refused, echoing the expected reflect.Struct kind versus what was given.
Source
Thrown at sqlx.go:875
return r.Err()
}
type rowsi interface {
Close() error
Columns() ([]string, error)
Err() error
Next() bool
Scan(...interface{}) error
}
// structOnlyError returns an error appropriate for type when a non-scannable
// struct is expected but something else is given
func structOnlyError(t reflect.Type) error {
isStruct := t.Kind() == reflect.Struct
isScanner := reflect.PtrTo(t).Implements(_scannerInterface)
if !isStruct {
return fmt.Errorf("expected %s but got %s", reflect.Struct, t.Kind())
}
if isScanner {
return fmt.Errorf("structscan expects a struct dest but the provided struct type %s implements scanner", t.Name())
}
return fmt.Errorf("expected a struct, but struct %s has no exported fields", t.Name())
}
// scanAll scans all rows into a destination, which must be a slice of any
// type. It resets the slice length to zero before appending each element to
// the slice. If the destination slice type is a Struct, then StructScan will
// be used on each row. If the destination is some other kind of base type,
// then each row must only have one column which can scan into that type. This
// allows you to do something like:
//
// rows, _ := db.Query("select id from people;")
// var ids []int
// scanAll(rows, &ids, false)
//View on GitHub (pinned to 41dac167fd)
Solutions
- Pass a pointer to a struct (e.g. &User{}) as the destination.
- Use rows.Scan or Get with a scalar dest if you intend to scan a single primitive column.
- Check the dest type before calling StructScan in generic code.
Example fix
// before
var name string
rows.StructScan(&name)
// after
type Row struct { Name string `db:"name"` }
var r Row
rows.StructScan(&r) Defensive patterns
Strategy: type-guard
Validate before calling
if reflectx.Deref(reflect.TypeOf(dest)).Kind() != reflect.Struct {
return errors.New("StructScan requires a struct destination")
} Type guard
func isStructDest(dest interface{}) bool {
t := reflect.TypeOf(dest)
return t != nil && t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
} Try / catch
if err := rows.StructScan(dest); err != nil {
if strings.Contains(err.Error(), "expected struct but got") {
return fmt.Errorf("wrong dest type for StructScan: %w", err)
}
return err
} Prevention
- Use Get/Scan for scalar destinations, StructScan only for structs.
- Type helper functions as *T where T is a struct.
- Avoid interface{} parameters for scan destinations in shared code.
When it happens
Trigger: Calling StructScan/Get/Select with a destination whose dereferenced kind is not reflect.Struct, forcing the non-struct branch of structOnlyError.
Common situations: Passing a *string, *int, or map instead of a struct pointer to StructScan; a helper function typed interface{} receiving the wrong dest; confusing Get (scalars allowed) with StructScan (struct required).
Related errors
- structscan expects a struct dest but the provided struct typ
- missing destination name %s in %T
- scannable dest type %s with >1 columns (%d) in result
- expected a struct, but struct %s has no exported fields
- non-statement type %v passed to Stmtx
AI-assisted analysis of jmoiron/sqlx@41dac167fd (2026-09-03).
Data as JSON: /api/errors/b87bbd3986c0e14e.
Report an issue: GitHub.