jmoiron/sqlx · error
argument not a struct
Error message
argument not a struct
What it means
fieldsByTraversal is used by StructScan-style helpers in sqlx to map database columns onto fields of a struct via reflectx traversals. It indirects the destination value and requires it to be (a pointer to) a struct; if the underlying kind is not struct it cannot receive the scanned fields, so it returns this error. The library throws it because column-to-field mapping is only meaningful for structs.
Source
Thrown at sqlx.go:1029
func baseType(t reflect.Type, expected reflect.Kind) (reflect.Type, error) {
t = reflectx.Deref(t)
if t.Kind() != expected {
return nil, fmt.Errorf("expected %s but got %s", expected, t.Kind())
}
return t, nil
}
// fieldsByName fills a values interface with fields from the passed value based
// on the traversals in int. If ptrs is true, return addresses instead of values.
// We write this instead of using FieldsByName to save allocations and map lookups
// when iterating over many rows. Empty traversals will get an interface pointer.
// Because of the necessity of requesting ptrs or values, it's considered a bit too
// specialized for inclusion in reflectx itself.
func fieldsByTraversal(v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error {
v = reflect.Indirect(v)
if v.Kind() != reflect.Struct {
return errors.New("argument not a struct")
}
for i, traversal := range traversals {
if len(traversal) == 0 {
values[i] = new(interface{})
continue
}
f := reflectx.FieldByIndexes(v, traversal)
if ptrs {
values[i] = f.Addr().Interface()
} else {
values[i] = f.Interface()
}
}
return nil
}
func missingFields(transversals [][]int) (field int, err error) {View on GitHub (pinned to 41dac167fd)
Solutions
- Ensure the destination passed to StructScan is a *struct (or pointer to slice of structs for Select).
- Use Get/Select/ScanRow for scalar or slice-of-scalar results instead of StructScan.
- Check that the pointer is non-nil before calling StructScan.
- Verify the reflect.Value being scanned actually points at a struct kind.
Example fix
// before
var count int
rows.StructScan(&count)
// after
var row struct{ Count int }
rows.StructScan(&row) Defensive patterns
Strategy: type-guard
Validate before calling
func isStructPtr(v interface{}) bool {
r := reflect.Indirect(reflect.ValueOf(v))
return r.Kind() == reflect.Struct && r.CanAddr()
}
// if !isStructPtr(dest) { use Get/Scan instead of StructScan } Type guard
func asStructPtr(v interface{}) (bool, string) {
r := reflect.Indirect(reflect.ValueOf(v))
if r.Kind() != reflect.Struct {
return false, fmt.Sprintf("got %s, want struct", r.Kind())
}
return true, ""
} Try / catch
var row MyStruct
if err := rows.StructScan(&row); err != nil {
if err.Error() == "argument not a struct" {
// fall back to rows.Scan(&scalar) for non-struct destinations
}
return err
} Prevention
- Always pass *T where T is a struct to StructScan.
- Use Get/Select/Scan for scalars and slices of scalars.
- Never pass a nil pointer destination to StructScan.
- Double-check you are not passing *[]T to StructScan — that is for Select.
When it happens
Trigger: Calling StructScan/Scan with a destination that is a pointer to a non-struct (e.g. *int, *[]string) or a nil pointer that reflects to a non-struct after Indirect, while the query returned columns (traversals were computed for a struct).
Common situations: Scanning a single-column result into *int via StructScan instead of db.Get(&intVar); passing **T where T is not a struct; passing an unaddressable value like map[string]interface{}; accidentally using StructScan on Rows for scalar queries.
Related errors
- missing field
- Incompatible type for GzippedText
- Incompatible type for JSONText
- bad []byte type assertion
- could not find name %s in %#v
AI-assisted analysis of jmoiron/sqlx@41dac167fd (2026-09-03).
Data as JSON: /api/errors/81bf7342bbdb206b.
Report an issue: GitHub.