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

  1. Pass a pointer to a struct (e.g. &User{}) as the destination.
  2. Use rows.Scan or Get with a scalar dest if you intend to scan a single primitive column.
  3. 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

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


AI-assisted analysis of jmoiron/sqlx@41dac167fd (2026-09-03). Data as JSON: /api/errors/b87bbd3986c0e14e. Report an issue: GitHub.