go-sql-driver/mysql · error

unsupported type %T, a

Error message

unsupported type %T, a %s

What it means

ConvertValue's final fallback: the value's reflect.Kind is not a pointer/int-family/uint-family/float/bool/slice/string - e.g. a struct, map, chan, func, array, or interface. The driver cannot serialize such a value to the MySQL protocol.

Solutions

  1. Pass a supported primitive field from the struct (e.g. s.ID instead of s).
  2. Implement driver.Valuer on the type to convert it to a supported primitive.
  3. Marshal the value to []byte/string (JSON) if it represents a complex payload.

Example fix

// before
type User struct{ ID int64; Name string }
stmt.Exec(User{1, "a"}) // SQL: INSERT INTO t VALUES (?)
// after
stmt.Exec(User{1, "a"}.ID)
Defensive patterns

Strategy: validation

Validate before calling

// Reject unsupported kinds before Exec.
func supportedKind(v any) bool {
    switch reflect.ValueOf(v).Kind() {
    case reflect.Struct, reflect.Map, reflect.Chan, reflect.Func, reflect.Array:
        return false
    }
    return true
}

Type guard

func isConvertible(v any) bool {
    if _, ok := v.(driver.Valuer); ok { return true }
    switch reflect.ValueOf(v).Kind() {
    case reflect.Struct, reflect.Map, reflect.Chan, reflect.Func, reflect.Array:
        return false
    }
    return true
}

Try / catch

// Returned from Exec/Query for unsupported argument kinds.
if _, err := stmt.Exec(args...); err != nil {
    if strings.Contains(err.Error(), "unsupported type") {
        // pass a primitive field or implement driver.Valuer
    }
}

Prevention

When it happens

Trigger: Passing a struct, map, chan, func, or array as a prepared-statement argument - e.g. passing a model struct instead of one of its scalar fields.

Common situations: Accidentally passing a whole model/config struct; plumbing the wrong variable into Exec.

Related errors


AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07). Data as JSON: /api/errors/c7a5dee657dea184. Report an issue: GitHub.

Appendix: source

Thrown at statement.go:211

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return rv.Uint(), nil
	case reflect.Float32, reflect.Float64:
		return rv.Float(), nil
	case reflect.Bool:
		return rv.Bool(), nil
	case reflect.Slice:
		switch t := rv.Type(); {
		case t == jsonType:
			return v, nil
		case t.Elem().Kind() == reflect.Uint8:
			return rv.Bytes(), nil
		default:
			return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
		}
	case reflect.String:
		return rv.String(), nil
	}
	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
}

var valuerReflectType = reflect.TypeFor[driver.Valuer]()

// callValuerValue returns vr.Value(), with one exception:
// If vr.Value is an auto-generated method on a pointer type and the
// pointer is nil, it would panic at runtime in the panicwrap
// method. Treat it like nil instead.
//
// This is so people can implement driver.Value on value types and
// still use nil pointers to those types to mean nil/NULL, just like
// string/*string.
//
// This is an exact copy of the same-named unexported function from the
// database/sql package.
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
	if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
		rv.IsNil() &&

View on GitHub (pinned to 03d76c7e07)